Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/policies/v1/policyserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ type PolicyServerSpec struct {
// +optional
Annotations map[string]string `json:"annotations,omitempty"`

// Labels is a map of custom labels to be applied to the Deployment created by the
// PolicyServer and to the Pods managed by that Deployment. System labels set by
// the controller always take precedence over user-defined labels with the same key.
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
// +optional
Labels map[string]string `json:"labels,omitempty"`

// List of environment variables to set in the container.
// +optional
Env []corev1.EnvVar `json:"env,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions api/policies/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,15 @@ spec:
items:
type: string
type: array
labels:
additionalProperties:
type: string
description: |-
Labels is a map of custom labels to be applied to the Deployment created by the
PolicyServer and to the Pods managed by that Deployment. System labels set by
the controller always take precedence over user-defined labels with the same key.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
type: object
limits:
additionalProperties:
anyOf:
Expand Down
11 changes: 10 additions & 1 deletion config/crd/bases/policies.kubewarden.io_policyservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,15 @@ spec:
items:
type: string
type: array
labels:
additionalProperties:
type: string
description: |-
Labels is a map of custom labels to be applied to the Deployment created by the
PolicyServer and to the Pods managed by that Deployment. System labels set by
the controller always take precedence over user-defined labels with the same key.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
type: object
limits:
additionalProperties:
anyOf:
Expand Down Expand Up @@ -1721,7 +1730,7 @@ spec:
Name of VerificationConfig configmap in the kubewarden namespace (same
namespace as the controller deployment), containing Sigstore verification
configuration. The configuration must be under a key named
verification-config in the Configmap.
verification-config in the ConfigMap.
type: string
required:
- image
Expand Down
5 changes: 5 additions & 0 deletions docs/crds/CRD-docs-for-docs-repo.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,11 @@ set by external tools to store and retrieve arbitrary metadata. They are not +
queryable and should be preserved when modifying objects. +
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + | | Optional: \{} +

| *`labels`* __object (keys:string, values:string)__ | Labels is a map of custom labels to be applied to the Deployment created by the +
PolicyServer and to the Pods managed by that Deployment. System labels set by +
the controller always take precedence over user-defined labels with the same key. +
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + | | Optional: \{} +

| *`env`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core[$$EnvVar$$] array__ | List of environment variables to set in the container. + | | Optional: \{} +

| *`serviceAccountName`* __string__ | Name of the service account associated with the policy server. +
Expand Down
1 change: 1 addition & 0 deletions docs/crds/CRD-docs-for-docs-repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ _Appears in:_
| `minAvailable` _[IntOrString](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#intorstring-intstr-util)_ | Number of policy server replicas that must be still available after the<br />eviction. The value can be an absolute number or a percentage. Only one of<br />MinAvailable or Max MaxUnavailable can be set. | | |
| `maxUnavailable` _[IntOrString](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#intorstring-intstr-util)_ | Number of policy server replicas that can be unavailable after the<br />eviction. The value can be an absolute number or a percentage. Only one of<br />MinAvailable or Max MaxUnavailable can be set. | | |
| `annotations` _object (keys:string, values:string)_ | Annotations is an unstructured key value map stored with a resource that may be<br />set by external tools to store and retrieve arbitrary metadata. They are not<br />queryable and should be preserved when modifying objects.<br />More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | | Optional: \{\} <br /> |
| `labels` _object (keys:string, values:string)_ | Labels is a map of custom labels to be applied to the Deployment created by the<br />PolicyServer and to the Pods managed by that Deployment. System labels set by<br />the controller always take precedence over user-defined labels with the same key.<br />More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ | | Optional: \{\} <br /> |
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#envvar-v1-core) array_ | List of environment variables to set in the container. | | Optional: \{\} <br /> |
| `serviceAccountName` _string_ | Name of the service account associated with the policy server.<br />Namespace service account will be used if not specified. | | Optional: \{\} <br /> |
| `imagePullSecret` _string_ | Name of ImagePullSecret secret in the same namespace, used for pulling<br />policies from repositories. | | Optional: \{\} <br /> |
Expand Down
47 changes: 30 additions & 17 deletions internal/controller/policyserver_controller_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"os"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -327,19 +328,28 @@ func configuresInsecureSources(policyServer *policiesv1.PolicyServer, admissionC
}

func configureLabelsAndAnnotations(policyServerDeployment *appsv1.Deployment, policyServer *policiesv1.PolicyServer, configMapVersion string) {
if policyServerDeployment.ObjectMeta.Annotations == nil {
policyServerDeployment.ObjectMeta.Annotations = make(map[string]string)
}
policyServerDeployment.ObjectMeta.Annotations[constants.PolicyServerDeploymentConfigVersionAnnotation] = configMapVersion

if policyServerDeployment.Labels == nil {
policyServerDeployment.Labels = make(map[string]string)
}
policyServerDeployment.Labels[constants.PolicyServerLabelKey] = policyServer.Name

// Build annotations from scratch each reconcile so that removed user annotations
// are not left behind on the Deployment metadata.
annotations := make(map[string]string, len(policyServer.Spec.Annotations)+1)
// Apply user-defined annotations first, then system annotations overwrite any conflicts.
for key, value := range policyServer.Spec.Annotations {
annotations[key] = value
}
annotations[constants.PolicyServerDeploymentConfigVersionAnnotation] = configMapVersion
policyServerDeployment.ObjectMeta.Annotations = annotations
Comment thread
flavio marked this conversation as resolved.
Outdated

// Build labels from scratch each reconcile so that removed user labels
// are not left behind on the Deployment metadata.
// Apply user-defined labels first, then system labels overwrite any conflicts.
labels := maps.Clone(policyServer.Spec.Labels)
if labels == nil {
labels = make(map[string]string)
Comment thread
flavio marked this conversation as resolved.
Outdated
}
labels[constants.PolicyServerLabelKey] = policyServer.Name
for key, value := range policyServer.CommonLabels() {
policyServerDeployment.Labels[key] = value
labels[key] = value
}
Comment thread
flavio marked this conversation as resolved.
policyServerDeployment.Labels = labels
}

func (r *PolicyServerReconciler) configureMutualTLS(ctx context.Context, policyServerDeployment *appsv1.Deployment) error {
Expand Down Expand Up @@ -417,12 +427,15 @@ func buildPolicyServerDeploymentSpec(
podSecurityContext *corev1.PodSecurityContext,
imagePullSecrets []corev1.LocalObjectReference,
) appsv1.DeploymentSpec {
templateLabels := map[string]string{
//nolint:staticcheck // this label will remove soon when policy lifecycle is revisited
constants.AppLabelKey: policyServer.AppLabel(),
constants.PolicyServerDeploymentPodSpecConfigVersionLabel: configMapVersion,
constants.PolicyServerLabelKey: policyServer.Name,
}
// Apply user-defined labels first, then system labels overwrite any conflicts.
templateLabels := maps.Clone(policyServer.Spec.Labels)
if templateLabels == nil {
templateLabels = make(map[string]string)
Comment thread
flavio marked this conversation as resolved.
}
//nolint:staticcheck // this label will remove soon when policy lifecycle is revisited
templateLabels[constants.AppLabelKey] = policyServer.AppLabel()
templateLabels[constants.PolicyServerDeploymentPodSpecConfigVersionLabel] = configMapVersion
templateLabels[constants.PolicyServerLabelKey] = policyServer.Name
for key, value := range policyServer.CommonLabels() {
templateLabels[key] = value
}
Expand Down
119 changes: 119 additions & 0 deletions internal/controller/policyserver_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,125 @@ var _ = Describe("PolicyServer controller", func() {
})))
})

It("should propagate custom labels from spec.labels to the Deployment and Pod template, with system labels taking precedence", func() {
policyServer := policiesv1.NewPolicyServerFactory().WithName(policyServerName).Build()
policyServer.Spec.Labels = map[string]string{
"custom-label": "custom-value",
"another-label": "another-value",
constants.PolicyServerLabelKey: "should-be-overridden",
"app.kubernetes.io/managed-by": "should-be-overridden",
}
createPolicyServerAndWaitForItsService(ctx, policyServer)

Eventually(func() error {
deployment, err := getTestPolicyServerDeployment(ctx, policyServerName)
if err != nil {
return err
}
// Custom labels should appear on Deployment ObjectMeta
Expect(deployment.ObjectMeta.Labels).To(HaveKeyWithValue("custom-label", "custom-value"))
Expect(deployment.ObjectMeta.Labels).To(HaveKeyWithValue("another-label", "another-value"))
// Custom labels should appear on Pod template
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(HaveKeyWithValue("custom-label", "custom-value"))
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(HaveKeyWithValue("another-label", "another-value"))
// System labels must not be overridden by user labels
Expect(deployment.ObjectMeta.Labels).To(HaveKeyWithValue(constants.PolicyServerLabelKey, policyServerName))
Expect(deployment.ObjectMeta.Labels).To(HaveKeyWithValue("app.kubernetes.io/managed-by", "kubewarden-controller"))
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(HaveKeyWithValue(constants.PolicyServerLabelKey, policyServerName))
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(HaveKeyWithValue("app.kubernetes.io/managed-by", "kubewarden-controller"))
return nil
}).Should(Succeed())
})

It("should propagate custom annotations from spec.annotations to the Deployment ObjectMeta and Pod template", func() {
policyServer := policiesv1.NewPolicyServerFactory().WithName(policyServerName).Build()
policyServer.Spec.Annotations = map[string]string{
"custom-annotation": "custom-value",
"another-annotation": "another-value",
}
createPolicyServerAndWaitForItsService(ctx, policyServer)

Eventually(func() error {
deployment, err := getTestPolicyServerDeployment(ctx, policyServerName)
if err != nil {
return err
}
// Custom annotations should appear on Deployment ObjectMeta
Expect(deployment.ObjectMeta.Annotations).To(HaveKeyWithValue("custom-annotation", "custom-value"))
Expect(deployment.ObjectMeta.Annotations).To(HaveKeyWithValue("another-annotation", "another-value"))
// Custom annotations should appear on Pod template
Expect(deployment.Spec.Template.ObjectMeta.Annotations).To(HaveKeyWithValue("custom-annotation", "custom-value"))
Expect(deployment.Spec.Template.ObjectMeta.Annotations).To(HaveKeyWithValue("another-annotation", "another-value"))
// System annotation must still be present
Expect(deployment.ObjectMeta.Annotations).To(HaveKey(constants.PolicyServerDeploymentConfigVersionAnnotation))
return nil
}).Should(Succeed())
})

It("should remove stale labels and annotations from the Deployment when they are removed from spec", func() {
policyServer := policiesv1.NewPolicyServerFactory().WithName(policyServerName).Build()
policyServer.Spec.Labels = map[string]string{"stale-label": "value"}
policyServer.Spec.Annotations = map[string]string{"stale-annotation": "value"}
createPolicyServerAndWaitForItsService(ctx, policyServer)

// Confirm they appear initially on both Deployment ObjectMeta and Pod template
Eventually(func() error {
deployment, err := getTestPolicyServerDeployment(ctx, policyServerName)
if err != nil {
return err
}
if _, ok := deployment.ObjectMeta.Labels["stale-label"]; !ok {
return errors.New("stale-label not yet present on Deployment ObjectMeta")
}
if _, ok := deployment.ObjectMeta.Annotations["stale-annotation"]; !ok {
return errors.New("stale-annotation not yet present on Deployment ObjectMeta")
}
if _, ok := deployment.Spec.Template.ObjectMeta.Labels["stale-label"]; !ok {
return errors.New("stale-label not yet present on Pod template")
}
if _, ok := deployment.Spec.Template.ObjectMeta.Annotations["stale-annotation"]; !ok {
return errors.New("stale-annotation not yet present on Pod template")
}
return nil
}, timeout, pollInterval).Should(Succeed())

// Remove both from spec
Eventually(func() error {
ps, err := getTestPolicyServer(ctx, policyServerName)
if err != nil {
return err
}
ps.Spec.Labels = nil
ps.Spec.Annotations = nil
return k8sClient.Update(ctx, ps)
}, timeout, pollInterval).Should(Succeed())

// Verify they are gone from both Deployment ObjectMeta and Pod template
Eventually(func() error {
deployment, err := getTestPolicyServerDeployment(ctx, policyServerName)
if err != nil {
return err
}
if _, ok := deployment.ObjectMeta.Labels["stale-label"]; ok {
return errors.New("stale-label still present on Deployment ObjectMeta after removal from spec")
}
if _, ok := deployment.ObjectMeta.Annotations["stale-annotation"]; ok {
return errors.New("stale-annotation still present on Deployment ObjectMeta after removal from spec")
}
if _, ok := deployment.Spec.Template.ObjectMeta.Labels["stale-label"]; ok {
return errors.New("stale-label still present on Pod template after removal from spec")
}
if _, ok := deployment.Spec.Template.ObjectMeta.Annotations["stale-annotation"]; ok {
return errors.New("stale-annotation still present on Pod template after removal from spec")
}
// System labels and annotations must still be present
Expect(deployment.ObjectMeta.Labels).To(HaveKey(constants.PolicyServerLabelKey))
Expect(deployment.ObjectMeta.Annotations).To(HaveKey(constants.PolicyServerDeploymentConfigVersionAnnotation))
Expect(deployment.Spec.Template.ObjectMeta.Labels).To(HaveKey(constants.PolicyServerLabelKey))
return nil
}, timeout, pollInterval).Should(Succeed())
})

It("should set the configMap version as a deployment annotation", func() {
policyServer := policiesv1.NewPolicyServerFactory().WithName(policyServerName).Build()
createPolicyServerAndWaitForItsService(ctx, policyServer)
Expand Down