Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
apiVersion: apiextensions.k8s.io/v1 # Hack because controller-gen complains if we don't have this
name: "Ingress"
crdName: ingresses.config.openshift.io
featureGates:
- IngressComponentRouteLabels
tests:
onCreate:
- name: Should be able to create an Ingress with componentRoutes containing labels
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-console
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-console
- name: Should be able to create an Ingress with multiple componentRoutes with labels
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console-2
namespace: openshift-console
hostname: console.internal.corp.example.com
labels:
ingress: shard-console2
- name: console-3
namespace: openshift-console
hostname: console.private.corp.example.com
labels:
ingress: shard-console3
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console-2
namespace: openshift-console
hostname: console.internal.corp.example.com
labels:
ingress: shard-console2
- name: console-3
namespace: openshift-console
hostname: console.private.corp.example.com
labels:
ingress: shard-console3
- name: Should be able to create componentRoutes with a DNS-prefixed label key
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
example.com/my-label: value
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
example.com/my-label: value
- name: Should be able to create componentRoutes with max-length label value
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
my-label: abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
my-label: abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0
- name: Should reject componentRoutes with invalid label value
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: "invalid value with spaces!"
expectedError: "Invalid value"
- name: Should accept componentRoutes with empty string label value
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: ""
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: ""
- name: Should accept componentRoutes with non-reserved prefix like openshift.io
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
openshift.io/my-label: value
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
openshift.io/my-label: value
- name: Should reject componentRoutes with invalid label key
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
"!!!bad": value
expectedError: "label keys must be valid qualified names"
- name: Should reject componentRoutes with reserved kubernetes.io/ label prefix
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
kubernetes.io/name: value
expectedError: "kubernetes.io/ and k8s.io/ prefixed label keys are reserved"
- name: Should reject componentRoutes with reserved k8s.io/ label prefix
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
k8s.io/name: value
expectedError: "kubernetes.io/ and k8s.io/ prefixed label keys are reserved"
- name: Should reject componentRoutes with label key name over 63 characters
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: value
expectedError: "label keys must be valid qualified names"
- name: Should reject componentRoutes with label value starting with dash
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: "-starts-with-dash"
expectedError: "Invalid value"
- name: Should reject componentRoutes with more than 8 labels
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
label1: value1
label2: value2
label3: value3
label4: value4
label5: value5
label6: value6
label7: value7
label8: value8
label9: value9
expectedError: "must have at most 8 items"
onUpdate:
Copy link
Copy Markdown
Member

@saschagrunert saschagrunert May 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests cover basic CRUD and some negatives but the CEL validations need more coverage. Please add:

  • Empty string value (ingress: "") accepted (valid per K8s label spec, non-obvious edge case)
  • Key name part over 63 characters rejected (tests format.qualifiedName() enforcement)
  • Value with valid characters in invalid position (e.g., -starts-with-dash) rejected
  • Non-reserved prefix accepted (e.g., openshift.io/my-label), confirms the reserved check is not overly broad

The MinProperties/MaxProperties boundary tests are basic OpenAPI and do not need separate test cases.

- name: Should be able to add labels to componentRoutes on update
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
updated: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-console
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-console
- name: Should be able to change an existing label value on update
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-old
updated: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-new
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-new
- name: Should be able to remove labels from componentRoutes on update
initial: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
labels:
ingress: shard-console
updated: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
expected: |
apiVersion: config.openshift.io/v1
kind: Ingress
spec:
domain: apps.example.com
componentRoutes:
- name: console
namespace: openshift-console
hostname: console.example.com
26 changes: 26 additions & 0 deletions config/v1/types_ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type IngressSpec struct {
// +listType=map
// +listMapKey=namespace
// +listMapKey=name
// +kubebuilder:validation:MaxItems=250
ComponentRoutes []ComponentRouteSpec `json:"componentRoutes,omitempty"`

// requiredHSTSPolicies specifies HSTS policies that are required to be set on newly created or updated routes
Expand Down Expand Up @@ -164,6 +165,13 @@ const (
Classic AWSLBType = "Classic"
)

// LabelValue is the value part of a Kubernetes label.
// A label value must be 0-63 characters, consisting of alphanumeric characters,
// '-', '_', or '.', and must start and end with an alphanumeric character.
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:XValidation:rule="!format.labelValue().validate(self).hasValue()",message="label values must be valid Kubernetes label values (at most 63 characters, alphanumeric, '-', '_', or '.', must start and end with alphanumeric)"
type LabelValue string

// ConsumingUser is an alias for string which we add validation to. Currently only service accounts are supported.
// +kubebuilder:validation:Pattern="^system:serviceaccount:[a-z0-9]([-a-z0-9]*[a-z0-9])?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
// +kubebuilder:validation:MinLength=1
Expand Down Expand Up @@ -245,6 +253,24 @@ type ComponentRouteSpec struct {
// the Secret specification for a serving certificate will not be needed.
// +optional
ServingCertKeyPairSecret SecretNameReference `json:"servingCertKeyPairSecret"`

// labels defines additional labels to be applied to the route created
// for the component. These labels are used by the IngressController to
// determine which routes it should manage. Changing labels may cause the
// route to be reassigned to a different IngressController.
// When omitted, no additional labels are applied to the component route.
// Label keys and values must conform to Kubernetes label conventions.
// Label keys must be valid qualified names per Kubernetes label conventions.
// Keys with the "kubernetes.io/" and "k8s.io/" prefixes are reserved and may not be used.
// When specified, labels must contain at least one entry, up to a maximum of 8.
// +openshift:enable:FeatureGate=IngressComponentRouteLabels
// +optional
// +mapType=granular
// +kubebuilder:validation:MinProperties=1
// +kubebuilder:validation:MaxProperties=8
Comment thread
Leo6Leo marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// +kubebuilder:validation:XValidation:rule="self.all(key, !format.qualifiedName().validate(key).hasValue())",message="label keys must be valid qualified names"
// +kubebuilder:validation:XValidation:rule="self.all(key, !key.startsWith('kubernetes.io/') && !key.startsWith('k8s.io/'))",message="kubernetes.io/ and k8s.io/ prefixed label keys are reserved and may not be used"
Labels map[string]LabelValue `json:"labels,omitempty"`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no key validation at the CRD level. Users can submit invalid keys (e.g. "!!!": "value") and only get an error asynchronously when the controller creates the Route. Add CEL rules for synchronous validation:

// +kubebuilder:validation:XValidation:rule="self.all(key, !format.qualifiedName().validate(key).hasValue())",message="label keys must be valid qualified names"
// +kubebuilder:validation:XValidation:rule="self.all(key, !key.startsWith('kubernetes.io/') && !key.startsWith('k8s.io/'))",message="kubernetes.io/ and k8s.io/ prefixed label keys are reserved and may not be used"

}

// ComponentRouteStatus contains information allowing configuration of a route's hostname and serving certificate.
Expand Down
Loading