From 751d5ede471ff14647d2e06182a2701e920e43e2 Mon Sep 17 00:00:00 2001 From: Abhijeet Sadawarte Date: Wed, 27 May 2026 20:54:03 +0530 Subject: [PATCH] operator/v1: replace anyOf with CEL XValidation for maxConnections The maxConnections field on IngressControllerTuningOptions used an anyOf pattern in the manual-override CRD manifest to validate that values are either sentinel values (-1, 0) or in the range 2000-2000000. When both branches of the anyOf failed, the API server only reported the enum branch error ("must be one of: -1, 0"), omitting the valid range and producing an incomplete error message. Replace the anyOf with a CEL XValidation rule on the MaxConnections field, which produces a clear, unified error message: "maxConnections must be 0, -1, or between 2000 and 2000000" Add integration tests for valid values (-1, 2000, 50000, 2000000), invalid values (1, 500, 1999, 3000000), and validation ratcheting (update to valid, update to another invalid, retain invalid while updating other fields). Bug: https://issues.redhat.com/browse/OCPBUGS-86570 Assisted-by: Claude Code --- .../AAA_ungated.yaml | 12 - .../AAA_ungated.yaml | 225 ++++++++++++++++++ operator/v1/types_ingress.go | 1 + ...ngresscontrollers-CustomNoUpgrade.crd.yaml | 14 +- ...ess_00_ingresscontrollers-Default.crd.yaml | 14 +- ...sscontrollers-DevPreviewNoUpgrade.crd.yaml | 14 +- ...ingress_00_ingresscontrollers-OKD.crd.yaml | 14 +- ...scontrollers-TechPreviewNoUpgrade.crd.yaml | 14 +- .../AAA_ungated.yaml | 3 + ...ControllerDynamicConfigurationManager.yaml | 3 + 10 files changed, 247 insertions(+), 67 deletions(-) diff --git a/operator/v1/manual-override-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml b/operator/v1/manual-override-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml index a404d61c85e..cbe58074aeb 100644 --- a/operator/v1/manual-override-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml +++ b/operator/v1/manual-override-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml @@ -26,18 +26,6 @@ spec: - properties: address: format: ipv6 - tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 subresources: scale: labelSelectorPath: .status.selector diff --git a/operator/v1/tests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml b/operator/v1/tests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml index 8ebe48b6de6..bb5dd58f7b0 100644 --- a/operator/v1/tests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml +++ b/operator/v1/tests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml @@ -678,6 +678,138 @@ tests: tuningOptions: httpKeepAliveTimeout: 0.001ms expectedError: "IngressController.operator.openshift.io \"default\" is invalid: spec.tuningOptions.httpKeepAliveTimeout: Invalid value: \"string\": httpKeepAliveTimeout must be greater than or equal to 1 millisecond" + - name: Should be able to create an IngressController with maxConnections set to -1 + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: -1 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: -1 + - name: Should be able to create an IngressController with maxConnections at minimum range + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 2000 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: 2000 + - name: Should be able to create an IngressController with maxConnections at maximum range + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 2000000 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: 2000000 + - name: Should be able to create an IngressController with maxConnections in mid range + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 50000 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: 50000 + - name: Should not be able to create an IngressController with maxConnections below minimum range + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 500 + expectedError: "maxConnections must be 0, -1, or between 2000 and 2000000" + - name: Should not be able to create an IngressController with maxConnections above maximum range + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 3000000 + expectedError: "maxConnections must be 0, -1, or between 2000 and 2000000" + - name: Should not be able to create an IngressController with maxConnections of 1 + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 1 + expectedError: "maxConnections must be 0, -1, or between 2000 and 2000000" + - name: Should not be able to create an IngressController with maxConnections of 1999 + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: default + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 1999 + expectedError: "maxConnections must be 0, -1, or between 2000 and 2000000" - name: Should be able to create an IngressController with valid domain initial: | apiVersion: operator.openshift.io/v1 @@ -830,3 +962,96 @@ tests: closedClientConnectionPolicy: Continue domain: "*.foo.com" replicas: 3 + - name: Should be able to update invalid maxConnections to a valid value + initialCRDPatches: + - op: remove + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/tuningOptions/properties/maxConnections/x-kubernetes-validations + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 500 + updated: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 50000 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: 50000 + - name: Should not be able to update invalid maxConnections to another invalid value + initialCRDPatches: + - op: remove + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/tuningOptions/properties/maxConnections/x-kubernetes-validations + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 500 + updated: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 999 + expectedError: "maxConnections must be 0, -1, or between 2000 and 2000000" + - name: Should be able to update other fields while retaining invalid maxConnections due to ratcheting + initialCRDPatches: + - op: remove + path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/tuningOptions/properties/maxConnections/x-kubernetes-validations + initial: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 500 + updated: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + tuningOptions: + maxConnections: 500 + replicas: 3 + expected: | + apiVersion: operator.openshift.io/v1 + kind: IngressController + metadata: + name: ic-maxconn-test + namespace: openshift-ingress-operator + spec: + httpEmptyRequestsPolicy: Respond + idleConnectionTerminationPolicy: Immediate + closedClientConnectionPolicy: Continue + tuningOptions: + maxConnections: 500 + replicas: 3 diff --git a/operator/v1/types_ingress.go b/operator/v1/types_ingress.go index 0c5cf919e15..376bfacde47 100644 --- a/operator/v1/types_ingress.go +++ b/operator/v1/types_ingress.go @@ -2034,6 +2034,7 @@ type IngressControllerTuningOptions struct { // processes in router containers with the following metric: // 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. // + // +kubebuilder:validation:XValidation:rule="self == 0 || self == -1 || (self >= 2000 && self <= 2000000)",message="maxConnections must be 0, -1, or between 2000 and 2000000" // +optional MaxConnections int32 `json:"maxConnections,omitempty"` diff --git a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-CustomNoUpgrade.crd.yaml b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-CustomNoUpgrade.crd.yaml index fdf10772dd8..334a2ca5a93 100644 --- a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-CustomNoUpgrade.crd.yaml +++ b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-CustomNoUpgrade.crd.yaml @@ -2118,17 +2118,6 @@ spec: type: string type: object tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 description: |- tuningOptions defines parameters for adjusting the performance of ingress controller pods. All fields are optional and will use their @@ -2341,6 +2330,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-Default.crd.yaml b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-Default.crd.yaml index 97c3ca8c401..e0c87dbb88a 100644 --- a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-Default.crd.yaml +++ b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-Default.crd.yaml @@ -2118,17 +2118,6 @@ spec: type: string type: object tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 description: |- tuningOptions defines parameters for adjusting the performance of ingress controller pods. All fields are optional and will use their @@ -2310,6 +2299,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-DevPreviewNoUpgrade.crd.yaml b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-DevPreviewNoUpgrade.crd.yaml index 89c366cda45..426b2200791 100644 --- a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-DevPreviewNoUpgrade.crd.yaml +++ b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-DevPreviewNoUpgrade.crd.yaml @@ -2118,17 +2118,6 @@ spec: type: string type: object tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 description: |- tuningOptions defines parameters for adjusting the performance of ingress controller pods. All fields are optional and will use their @@ -2341,6 +2330,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-OKD.crd.yaml b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-OKD.crd.yaml index 535ddf0bc87..883fb3c3486 100644 --- a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-OKD.crd.yaml +++ b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-OKD.crd.yaml @@ -2118,17 +2118,6 @@ spec: type: string type: object tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 description: |- tuningOptions defines parameters for adjusting the performance of ingress controller pods. All fields are optional and will use their @@ -2310,6 +2299,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-TechPreviewNoUpgrade.crd.yaml b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-TechPreviewNoUpgrade.crd.yaml index 2fbc3cd4e39..c5abd2c013e 100644 --- a/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-TechPreviewNoUpgrade.crd.yaml +++ b/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers-TechPreviewNoUpgrade.crd.yaml @@ -2118,17 +2118,6 @@ spec: type: string type: object tuningOptions: - anyOf: - - properties: - maxConnections: - enum: - - -1 - - 0 - - properties: - maxConnections: - format: int32 - maximum: 2000000 - minimum: 2000 description: |- tuningOptions defines parameters for adjusting the performance of ingress controller pods. All fields are optional and will use their @@ -2341,6 +2330,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml b/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml index db97e59b3f6..625357374f7 100644 --- a/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml +++ b/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/AAA_ungated.yaml @@ -2292,6 +2292,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload diff --git a/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/IngressControllerDynamicConfigurationManager.yaml b/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/IngressControllerDynamicConfigurationManager.yaml index 883dd6d1ecb..41fd657350d 100644 --- a/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/IngressControllerDynamicConfigurationManager.yaml +++ b/operator/v1/zz_generated.featuregated-crd-manifests/ingresscontrollers.operator.openshift.io/IngressControllerDynamicConfigurationManager.yaml @@ -2323,6 +2323,9 @@ spec: 'container_memory_working_set_bytes{container="router",namespace="openshift-ingress"}/container_processes{container="router",namespace="openshift-ingress"}'. format: int32 type: integer + x-kubernetes-validations: + - message: maxConnections must be 0, -1, or between 2000 and 2000000 + rule: self == 0 || self == -1 || (self >= 2000 && self <= 2000000) reloadInterval: description: |- reloadInterval defines the minimum interval at which the router is allowed to reload