diff --git a/config/v1/tests/ingresses.config.openshift.io/AAA_ungated.yaml b/config/v1/tests/ingresses.config.openshift.io/AAA_ungated.yaml index 3d966de4eb1..0f5567d5617 100644 --- a/config/v1/tests/ingresses.config.openshift.io/AAA_ungated.yaml +++ b/config/v1/tests/ingresses.config.openshift.io/AAA_ungated.yaml @@ -23,6 +23,129 @@ tests: kind: Ingress spec: domain: apps.example.com + - 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 empty labels map + initial: | + apiVersion: config.openshift.io/v1 + kind: Ingress + spec: + domain: apps.example.com + componentRoutes: + - name: console + namespace: openshift-console + hostname: console.example.com + labels: {} + expected: | + apiVersion: config.openshift.io/v1 + kind: Ingress + spec: + domain: apps.example.com + componentRoutes: + - name: console + namespace: openshift-console + hostname: console.example.com + - 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 Kubernetes label keys" + - 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: "label values must be valid Kubernetes label values" + - 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: "Too many properties" onUpdate: - name: Should not be able to change domain once set initial: | @@ -54,3 +177,35 @@ tests: spec: domain: apps.example.com appsDomain: custom.example.com + - 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 diff --git a/config/v1/types_ingress.go b/config/v1/types_ingress.go index 26e0ebf2184..9c6c1e0e3c5 100644 --- a/config/v1/types_ingress.go +++ b/config/v1/types_ingress.go @@ -245,6 +245,20 @@ 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. + // Label keys and values must conform to Kubernetes label conventions: + // keys must be 1-63 characters (with optional prefix up to 253 characters), + // and values must be 0-63 characters, consisting of alphanumeric characters, + // '-', '_', or '.', and must start and end with an alphanumeric character. + // +optional + // +mapType=granular + // +kubebuilder:validation:MaxProperties=8 + // +kubebuilder:validation:XValidation:rule="self.all(key, key.matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*[/])?([A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?)$'))",message="label keys must be valid Kubernetes label keys" + // +kubebuilder:validation:XValidation:rule="self.all(key, self[key].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9])?$'))",message="label values must be valid Kubernetes label values (at most 63 characters, alphanumeric, '-', '_', or '.', must start and end with alphanumeric)" + Labels map[string]string `json:"labels,omitempty"` } // ComponentRouteStatus contains information allowing configuration of a route's hostname and serving certificate. diff --git a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_ingresses.crd.yaml b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_ingresses.crd.yaml index 603d58d6d8d..5ea4291fc5b 100644 --- a/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_ingresses.crd.yaml +++ b/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_ingresses.crd.yaml @@ -75,6 +75,27 @@ spec: the route. pattern: ^([a-zA-Z0-9\p{S}\p{L}]((-?[a-zA-Z0-9\p{S}\p{L}]{0,62})?)|([a-zA-Z0-9\p{S}\p{L}](([a-zA-Z0-9-\p{S}\p{L}]{0,61}[a-zA-Z0-9\p{S}\p{L}])?)(\.)){1,}([a-zA-Z\p{L}]){2,63})$|^(([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})[\.]){0,}([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})$ type: string + labels: + additionalProperties: + type: string + description: |- + 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. + Label keys and values must conform to Kubernetes label conventions: + keys must be 1-63 characters (with optional prefix up to 253 characters), + and values must be 0-63 characters, consisting of alphanumeric characters, + '-', '_', or '.', and must start and end with an alphanumeric character. + maxProperties: 8 + type: object + x-kubernetes-map-type: granular + x-kubernetes-validations: + - message: label keys must be valid Kubernetes label keys + rule: self.all(key, key.matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*[/])?([A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?)$')) + - message: label values must be valid Kubernetes label values + (at most 63 characters, alphanumeric, '-', '_', or '.', + must start and end with alphanumeric) + rule: self.all(key, self[key].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9])?$')) name: description: |- name is the logical name of the route to customize. diff --git a/config/v1/zz_generated.deepcopy.go b/config/v1/zz_generated.deepcopy.go index 1cb3cceaef4..d744a50f093 100644 --- a/config/v1/zz_generated.deepcopy.go +++ b/config/v1/zz_generated.deepcopy.go @@ -1492,6 +1492,13 @@ func (in *ComponentOverride) DeepCopy() *ComponentOverride { func (in *ComponentRouteSpec) DeepCopyInto(out *ComponentRouteSpec) { *out = *in out.ServingCertKeyPairSecret = in.ServingCertKeyPairSecret + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } @@ -3671,7 +3678,9 @@ func (in *IngressSpec) DeepCopyInto(out *IngressSpec) { if in.ComponentRoutes != nil { in, out := &in.ComponentRoutes, &out.ComponentRoutes *out = make([]ComponentRouteSpec, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.RequiredHSTSPolicies != nil { in, out := &in.RequiredHSTSPolicies, &out.RequiredHSTSPolicies diff --git a/config/v1/zz_generated.featuregated-crd-manifests/ingresses.config.openshift.io/AAA_ungated.yaml b/config/v1/zz_generated.featuregated-crd-manifests/ingresses.config.openshift.io/AAA_ungated.yaml index a613cf562ce..1d15590ca11 100644 --- a/config/v1/zz_generated.featuregated-crd-manifests/ingresses.config.openshift.io/AAA_ungated.yaml +++ b/config/v1/zz_generated.featuregated-crd-manifests/ingresses.config.openshift.io/AAA_ungated.yaml @@ -76,6 +76,27 @@ spec: the route. pattern: ^([a-zA-Z0-9\p{S}\p{L}]((-?[a-zA-Z0-9\p{S}\p{L}]{0,62})?)|([a-zA-Z0-9\p{S}\p{L}](([a-zA-Z0-9-\p{S}\p{L}]{0,61}[a-zA-Z0-9\p{S}\p{L}])?)(\.)){1,}([a-zA-Z\p{L}]){2,63})$|^(([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})[\.]){0,}([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})$ type: string + labels: + additionalProperties: + type: string + description: |- + 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. + Label keys and values must conform to Kubernetes label conventions: + keys must be 1-63 characters (with optional prefix up to 253 characters), + and values must be 0-63 characters, consisting of alphanumeric characters, + '-', '_', or '.', and must start and end with an alphanumeric character. + maxProperties: 8 + type: object + x-kubernetes-map-type: granular + x-kubernetes-validations: + - message: label keys must be valid Kubernetes label keys + rule: self.all(key, key.matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*[/])?([A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?)$')) + - message: label values must be valid Kubernetes label values + (at most 63 characters, alphanumeric, '-', '_', or '.', + must start and end with alphanumeric) + rule: self.all(key, self[key].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9])?$')) name: description: |- name is the logical name of the route to customize. diff --git a/config/v1/zz_generated.swagger_doc_generated.go b/config/v1/zz_generated.swagger_doc_generated.go index 02900dbab8d..6503cdec5fd 100644 --- a/config/v1/zz_generated.swagger_doc_generated.go +++ b/config/v1/zz_generated.swagger_doc_generated.go @@ -2151,6 +2151,7 @@ var map_ComponentRouteSpec = map[string]string{ "name": "name is the logical name of the route to customize.\n\nThe namespace and name of this componentRoute must match a corresponding entry in the list of status.componentRoutes if the route is to be customized.", "hostname": "hostname is the hostname that should be used by the route.", "servingCertKeyPairSecret": "servingCertKeyPairSecret is a reference to a secret of type `kubernetes.io/tls` in the openshift-config namespace. The serving cert/key pair must match and will be used by the operator to fulfill the intent of serving with this name. If the custom hostname uses the default routing suffix of the cluster, the Secret specification for a serving certificate will not be needed.", + "labels": "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. Label keys and values must conform to Kubernetes label conventions: keys must be 1-63 characters (with optional prefix up to 253 characters), and values must be 0-63 characters, consisting of alphanumeric characters, '-', '_', or '.', and must start and end with an alphanumeric character.", } func (ComponentRouteSpec) SwaggerDoc() map[string]string { diff --git a/openapi/generated_openapi/zz_generated.openapi.go b/openapi/generated_openapi/zz_generated.openapi.go index 6f65ddbfdf0..0c9bb3b08a5 100644 --- a/openapi/generated_openapi/zz_generated.openapi.go +++ b/openapi/generated_openapi/zz_generated.openapi.go @@ -11870,6 +11870,27 @@ func schema_openshift_api_config_v1_ComponentRouteSpec(ref common.ReferenceCallb Ref: ref("github.com/openshift/api/config/v1.SecretNameReference"), }, }, + "labels": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-map-type": "granular", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "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. Label keys and values must conform to Kubernetes label conventions: keys must be 1-63 characters (with optional prefix up to 253 characters), and values must be 0-63 characters, consisting of alphanumeric characters, '-', '_', or '.', and must start and end with an alphanumeric character.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, Required: []string{"namespace", "name", "hostname"}, }, diff --git a/payload-manifests/crds/0000_10_config-operator_01_ingresses.crd.yaml b/payload-manifests/crds/0000_10_config-operator_01_ingresses.crd.yaml index 603d58d6d8d..5ea4291fc5b 100644 --- a/payload-manifests/crds/0000_10_config-operator_01_ingresses.crd.yaml +++ b/payload-manifests/crds/0000_10_config-operator_01_ingresses.crd.yaml @@ -75,6 +75,27 @@ spec: the route. pattern: ^([a-zA-Z0-9\p{S}\p{L}]((-?[a-zA-Z0-9\p{S}\p{L}]{0,62})?)|([a-zA-Z0-9\p{S}\p{L}](([a-zA-Z0-9-\p{S}\p{L}]{0,61}[a-zA-Z0-9\p{S}\p{L}])?)(\.)){1,}([a-zA-Z\p{L}]){2,63})$|^(([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})[\.]){0,}([a-z0-9][-a-z0-9]{0,61}[a-z0-9]|[a-z0-9]{1,63})$ type: string + labels: + additionalProperties: + type: string + description: |- + 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. + Label keys and values must conform to Kubernetes label conventions: + keys must be 1-63 characters (with optional prefix up to 253 characters), + and values must be 0-63 characters, consisting of alphanumeric characters, + '-', '_', or '.', and must start and end with an alphanumeric character. + maxProperties: 8 + type: object + x-kubernetes-map-type: granular + x-kubernetes-validations: + - message: label keys must be valid Kubernetes label keys + rule: self.all(key, key.matches('^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*[/])?([A-Za-z0-9]([-A-Za-z0-9_.]{0,61}[A-Za-z0-9])?)$')) + - message: label values must be valid Kubernetes label values + (at most 63 characters, alphanumeric, '-', '_', or '.', + must start and end with alphanumeric) + rule: self.all(key, self[key].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9])?$')) name: description: |- name is the logical name of the route to customize.