This issue is a followup on #1212 (comment):
So the OpenAPI based OneOf/AnyOf constraints would be a good alternative despite the unfriendly error message.
Yeah so perhaps we take that route of a field on the marker useOpenAPIValidation=true or something like that. @\alvaroaleman any opinions?
With deeply nested types containing arrays, I have seen CEL break down. In most cases with newer k8s versions, MaxItems should suffice
Yeah I guess when adding a compatible to change to an existing API, the argument for bounding all parents can be more difficult
PR #1212 added AtMostOneOf, ExactlyOneOf, and AtLeastOneOf markers, implemented with x-kubernetes-validations (CEL) for better error messages. An option to have those generate OpenAPI oneOf/anyOf would allow avoiding CEL budget limitations.
Context
In the cilium project, we have the CiliumNetworkPolicy CRD that uses oneOf-style validation in several places. One of the most problematic ones for the CEL implementation is CIDRRule, which sits inside the ingress[]/egress[]/ingressDeny[]/egressDeny[] rule lists:
// +kubebuilder:validation:ExactlyOneOf=cidr;cidrGroupRef;cidrGroupSelector
type CIDRRule struct {
Cidr CIDR `json:"cidr,omitempty"`
CIDRGroupRef CIDRGroupRef `json:"cidrGroupRef,omitempty"`
CIDRGroupSelector EndpointSelector `json:"cidrGroupSelector,omitzero"`
// ...
}
type IngressRule struct {
// ...
FromCIDRSet []CIDRRule `json:"fromCIDRSet,omitzero"`
}
type Rule struct {
Ingress []IngressRule `json:"ingress,omitempty"`
IngressDeny []IngressDenyRule `json:"ingressDeny,omitempty"`
Egress []EgressRule `json:"egress,omitempty"`
EgressDeny []EgressDenyRule `json:"egressDeny,omitempty"`
// ...
}
The generated CEL rule on CIDRRule is fine in isolation. But because CIDRRule is reachable through several layers of arrays, the rule is reapplied at every nesting level, so 8 separate sites in the schema each carry a copy of the same rule. The resulting CRD is rejected by the API server:
CustomResourceDefinition.apiextensions.k8s.io "ciliumnetworkpolicies.cilium.io" is invalid:
spec.validation.openAPIV3Schema.properties[spec].properties[ingress].items.properties[fromCIDRSet].items.x-kubernetes-validations[0].rule:
Forbidden: estimated rule cost exceeds budget by factor of 7.1x
...
spec.validation.openAPIV3Schema:
Forbidden: x-kubernetes-validations estimated rule cost total for entire OpenAPIv3 schema exceeds budget by factor of 12.7x
We did try optimizing the CEL expressions themselves (#1408) which managed to get each individual rule under budget, but the overall CRD cost still remained over budget.
We also looked at setting MaxItems to bound the array's costs, but in order for MaxItems to be effective, every single array in the chain needs to have it set and on a widely adopted stable (v2) CRD like the CiliumNetworkPolicy, we can't afford to add such constraints which would be a breaking change for users, just to get some validation.
I don't think cilium is the only project with this pattern of "polymorphic union under a list". Having the option to use openAPI validation instead of CEL for those cases would be beneficial. The cost would be a slightly worse error message for users but it's better than no validation at all.
Proposal
We'd be highly interested in having a way to generate openAPI validation for oneOf markers as was initially suggested in #1212:
// +kubebuilder:validation:ExactlyOneOf=cidr;cidrGroupRef;cidrGroupSelector,useOpenAPIValidation=true
When set, the marker would emit openAPI oneOf/anyOf/allOf instead of x-kubernetes-validations validation. The default behavior would remain CEL so existing users' error messages don't regress.
If controller-tools maintainers are open to this proposal, we'd be happy to contribute it (the actual implementation actually already exists as the first version of #1212 before it changed trajectory to use CEL instead of openAPI).
This issue is a followup on #1212 (comment):
PR #1212 added
AtMostOneOf,ExactlyOneOf, andAtLeastOneOfmarkers, implemented withx-kubernetes-validations(CEL) for better error messages. An option to have those generate OpenAPIoneOf/anyOfwould allow avoiding CEL budget limitations.Context
In the cilium project, we have the
CiliumNetworkPolicyCRD that uses oneOf-style validation in several places. One of the most problematic ones for the CEL implementation isCIDRRule, which sits inside the ingress[]/egress[]/ingressDeny[]/egressDeny[] rule lists:The generated CEL rule on
CIDRRuleis fine in isolation. But becauseCIDRRuleis reachable through several layers of arrays, the rule is reapplied at every nesting level, so 8 separate sites in the schema each carry a copy of the same rule. The resulting CRD is rejected by the API server:We did try optimizing the CEL expressions themselves (#1408) which managed to get each individual rule under budget, but the overall CRD cost still remained over budget.
We also looked at setting
MaxItemsto bound the array's costs, but in order forMaxItemsto be effective, every single array in the chain needs to have it set and on a widely adopted stable (v2) CRD like theCiliumNetworkPolicy, we can't afford to add such constraints which would be a breaking change for users, just to get some validation.I don't think cilium is the only project with this pattern of "polymorphic union under a list". Having the option to use openAPI validation instead of CEL for those cases would be beneficial. The cost would be a slightly worse error message for users but it's better than no validation at all.
Proposal
We'd be highly interested in having a way to generate openAPI validation for oneOf markers as was initially suggested in #1212:
// +kubebuilder:validation:ExactlyOneOf=cidr;cidrGroupRef;cidrGroupSelector,useOpenAPIValidation=trueWhen set, the marker would emit openAPI
oneOf/anyOf/allOfinstead ofx-kubernetes-validationsvalidation. The default behavior would remain CEL so existing users' error messages don't regress.If
controller-toolsmaintainers are open to this proposal, we'd be happy to contribute it (the actual implementation actually already exists as the first version of #1212 before it changed trajectory to use CEL instead of openAPI).