-
-
Notifications
You must be signed in to change notification settings - Fork 320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement derive(ValidateSchema)
macro for generating cel validation on CRDs
#1649
base: main
Are you sure you want to change the base?
Implement derive(ValidateSchema)
macro for generating cel validation on CRDs
#1649
Conversation
8a8cef3
to
f286169
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1649 +/- ##
=======================================
- Coverage 75.6% 75.2% -0.3%
=======================================
Files 82 83 +1
Lines 7405 7450 +45
=======================================
+ Hits 5591 5600 +9
- Misses 1814 1850 +36
|
047b626
to
06ea3e9
Compare
- Extend with supported values from docs - https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation-rules - Implement as Validated derive macro - Use the raw Rule for the validated attribute Signed-off-by: Danil-Grigorev <[email protected]>
06ea3e9
to
ee96ec4
Compare
derive(Validated)
macro for generated CRDs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments and questions. i think this is a pretty cool approach.
kube-core/src/validation.rs
Outdated
|
||
/// Reason is a machine-readable value providing more detail about why a field failed the validation. | ||
/// | ||
/// More in [docs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#field-reason) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly, I see these ones in the generated docs under https://github.com/kube-rs/k8s-pb/blob/ce4261fb52266f05cd7a06dbb8f4c0fcaa41c06a/k8s-pb/src/apiextensions_apiserver/pkg/apis/apiextensions/v1/mod.rs#L736 but because of bad go enum usage it's just a doc comment :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would not pretend I saw it being generated, but yeah, missing enum is better to have :). Maybe worth adding From/Into
conversion for ensuring compatibility.
kube-core/src/validation.rs
Outdated
/// Example: | ||
/// "must be a URL with the host matching spec.host" | ||
Message(String), | ||
/// Expression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these doc strings taken from anywhere?
kube/src/lib.rs
Outdated
@@ -168,6 +168,10 @@ pub use kube_derive::CustomResource; | |||
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] | |||
pub use kube_derive::Resource; | |||
|
|||
#[cfg(feature = "derive")] | |||
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] | |||
pub use kube_derive::Validated; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming wise, possibly this introduces an confusion possiblities with garde::Validate
- which we do advocate for
kube-derive/src/lib.rs
Outdated
/// #[kube(group = "kube.rs", version = "v1", kind = "Struct")] | ||
/// struct MyStruct { | ||
/// #[serde(default = "default")] | ||
/// #[validated(rule = Rule{rule: "self != ''".into(), message: Some("failure message".into()), ..Default::default()})] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pretty cool that you can do a full struct here like this. does it support shorthands as well if we were to create builders on Rule
? could lead to:
#[validated(rule = Rule::rule("self != ''").message("failure message")]
possibly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is possible, including something simple such as #[validated(rule = “self != ‘’”.into())]
.
kube-core/src/validation.rs
Outdated
/// Rule is a CEL validation rule for the CRD field | ||
#[derive(Default, Serialize, Deserialize, Clone)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Rule { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self; this may perhaps be churned through a cel crate against an object to perform validation client side. should investigate this later.
Signed-off-by: Danil-Grigorev <[email protected]>
Signed-off-by: Danil-Grigorev <[email protected]>
Signed-off-by: Danil-Grigorev <[email protected]>
Signed-off-by: Danil-Grigorev <[email protected]>
derive(Validated)
macro for generated CRDsderive(ValidateSchema)
macro for generated CRDs
derive(ValidateSchema)
macro for generated CRDsderive(ValidateSchema)
macro for generating cel validation on CRDs
Signed-off-by: Danil-Grigorev <[email protected]>
Signed-off-by: Danil-Grigorev <[email protected]>
Signed-off-by: Danil-Grigorev <[email protected]>
#[derive(CustomResource, Serialize, Deserialize, Debug, PartialEq, Clone, JsonSchema)] | ||
#[derive(CustomResource, Serialize, Deserialize, Debug, PartialEq, Clone, ValidateSchema)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea here now is to replace JsonSchema
with ValidateSchema
whenever we need to use rule
annotations? I see it's not a universal replacement, the sub-structs still use JsonSchema
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a wrapper on top of the JsonSchema
implementing it. Crate path for the JsonSchema can be overridden, same as in the CustomResource trait. Rule is one of the keywords which can be used here, there maybe more in the future, since the implementation is extensible and will not be affected by schemars
versions. Sub-schemas don’t need ValidateSchema
, if a rule is not used there. Implementing this would require to blanket implement the ValidateSchema
(trait, a new one) for all standard types, and since we only modify “this current schema I see now” I tried to keep it simple, and it also was the only path forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved towards implementing option 3 from comment, so current changes are direct descendants of this (and the fact that I realized how to work around unordered properties in the schema)
Motivation
Related to #1367
CRDs allow to declare server-side validation rules using CEL. This functionality is supported via
#[schemars(schema_with = "<schemagen-wrapper>")]
, but requires defining a method with handling logic, which may be error-prone.Since
kube
owns CRD generation code, the idea is to simplify this process for added validation rules and achieve more declarative approach, similar to thekubebuilder
library. This approach will be compatible withkopium
generation based on existing CRD structures, already using CEL expressions.Solution
Allow for a more native handling of CEL validation rules on the CRDs via a field macro.
This PR is a followup on #1621 which addresses some of the concerns.
JsonSchema
, which allows further additions to the schema later,struct
level validation rules and is not affected byschemars
version.kube::core
and invoking fromkube::derive
.Other things tried (TLDR)
Visitor trait:
It is possible to generate a newVisitor
implementation per each validation rule. But the problem with this approach is that the generation happens forValidator
derive on the structure, while theCustomResource
derive is responsible for populating additional visitors forcrd()
. There is no one specific method which can collect all visitors under one chain, invoked fromschemars
. This likely requires every individual field in each struct to implement theValidated
trait, involving creation of ashemars/serde
type of logic.Then theschemars Schema
has no indicators for the source structure in the schema within Visitor, so there is no way (without generatingschemars(title = “FooSpec”)
as a metadata) to match the added visitor on the processed object param to make modifications. It is possible to addpreserve_order
feature to schemars and “search” for the property of the structure, as long as the source struct name is mapped to theSchema
content.With generating
JsonSchema
visitor extensions for more complex scenarios are possible to explore in the future.Generating schemars attributes
While it is a viable option, such thing is not possible with
derive
macro, and has to useproc_macro
instead, This approach is additionally hiding the updates of derive attributes under the hood, which feels unintuitive, as it performs updates to the macro markers, meant to generate code. Explored in #1621