pub struct Validator { /* private fields */ }validation only.Expand description
Validates Kubernetes objects against CRD schema CEL validation rules.
Walks the OpenAPI schema tree, compiles x-kubernetes-validations rules at
each node, and evaluates them against the corresponding object values.
For repeated validation against the same schema, use compile_schema +
validate_compiled to avoid re-compilation.
§Thread Safety
Validator is Send and can be moved across threads.
Implementations§
Source§impl Validator
impl Validator
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new Validator with all K8s CEL functions pre-registered.
Examples found in repository?
14fn main() {
15 let schema = json!({
16 "type": "object",
17 "properties": {
18 "spec": {
19 "type": "object",
20 "properties": {
21 "replicas": {"type": "integer"}
22 },
23 "x-kubernetes-validations": [{
24 "rule": "self.replicas <= 5",
25 // Static fallback, used if messageExpression is absent/non-string.
26 "message": "too many replicas",
27 // Dynamic message: embeds the actual value and the limit.
28 "messageExpression":
29 "'requested ' + string(self.replicas) + ' replicas, max allowed is 5'"
30 }]
31 }
32 }
33 });
34
35 let validator = Validator::new();
36
37 // Two invalid objects: the dynamic message differs per object.
38 for replicas in [8, 20] {
39 let object = json!({"spec": {"replicas": replicas}});
40 let errors = validator.validate(&schema, &object, None);
41 for err in &errors {
42 // Note the message reflects this object's value, not a fixed string.
43 println!("replicas={replicas}: {}", err.message);
44 }
45 }
46
47 // A valid object produces no errors (and so no message at all).
48 let ok = json!({"spec": {"replicas": 3}});
49 println!(
50 "replicas=3: {} error(s)",
51 validator.validate(&schema, &ok, None).len()
52 );
53}More examples
17fn main() {
18 let schema = json!({
19 "type": "object",
20 "properties": {
21 "storageClass": {"type": "string"},
22 "replicas": {"type": "integer"}
23 },
24 "x-kubernetes-validations": [
25 // Immutable field: may never change once set.
26 {
27 "rule": "self.storageClass == oldSelf.storageClass",
28 "message": "storageClass is immutable"
29 },
30 // Monotonic: replicas may only grow (no scale-down).
31 {
32 "rule": "self.replicas >= oldSelf.replicas",
33 "message": "replicas cannot be decreased"
34 }
35 ]
36 });
37
38 let validator = Validator::new();
39 let stored = json!({"storageClass": "fast-ssd", "replicas": 3});
40
41 // CREATE: no previous state, so transition rules are skipped entirely.
42 let created = json!({"storageClass": "fast-ssd", "replicas": 3});
43 report("CREATE (old = None)", validator.validate(&schema, &created, None));
44
45 // UPDATE that respects both rules: same storageClass, scaling up.
46 let scale_up = json!({"storageClass": "fast-ssd", "replicas": 5});
47 report(
48 "UPDATE scale up 3->5",
49 validator.validate(&schema, &scale_up, Some(&stored)),
50 );
51
52 // UPDATE that violates the monotonic rule: scaling down.
53 let scale_down = json!({"storageClass": "fast-ssd", "replicas": 1});
54 report(
55 "UPDATE scale down 3->1",
56 validator.validate(&schema, &scale_down, Some(&stored)),
57 );
58
59 // UPDATE that mutates an immutable field.
60 let restorage = json!({"storageClass": "cheap-hdd", "replicas": 3});
61 report(
62 "UPDATE change storageClass",
63 validator.validate(&schema, &restorage, Some(&stored)),
64 );
65}8fn main() {
9 let schema = json!({
10 "type": "object",
11 "properties": {
12 "spec": {
13 "type": "object",
14 "properties": {
15 "replicas": {
16 "type": "integer",
17 "x-kubernetes-validations": [
18 {"rule": "self >= 0", "message": "replicas must be non-negative"}
19 ]
20 }
21 },
22 "x-kubernetes-validations": [
23 {"rule": "self.replicas >= 1", "message": "at least one replica required"}
24 ]
25 }
26 }
27 });
28
29 let validator = Validator::new();
30
31 // Valid object
32 let valid = json!({"spec": {"replicas": 3}});
33 let errors = validator.validate(&schema, &valid, None);
34 println!("Valid object: {} errors", errors.len());
35
36 // Invalid object
37 let invalid = json!({"spec": {"replicas": -1}});
38 let errors = validator.validate(&schema, &invalid, None);
39 println!("\nInvalid object: {} errors", errors.len());
40 for err in &errors {
41 println!(" [{path}] {msg}", path = err.field_path, msg = err.message);
42 }
43
44 // Transition rule (update check)
45 let transition_schema = json!({
46 "type": "object",
47 "x-kubernetes-validations": [{
48 "rule": "self.replicas >= oldSelf.replicas",
49 "message": "cannot scale down"
50 }],
51 "properties": {
52 "replicas": {"type": "integer"}
53 }
54 });
55
56 let new_obj = json!({"replicas": 2});
57 let old_obj = json!({"replicas": 5});
58
59 // Create (no old object): transition rule skipped
60 let errors = validator.validate(&transition_schema, &new_obj, None);
61 println!("\nCreate (no old): {} errors", errors.len());
62
63 // Update (scale down): transition rule fires
64 let errors = validator.validate(&transition_schema, &new_obj, Some(&old_obj));
65 println!("Update (scale down): {} errors", errors.len());
66 for err in &errors {
67 println!(" {}", err);
68 }
69}19fn main() {
20 // In a real controller this comes from the cluster, e.g.:
21 // use kube::{Api, Client};
22 // use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
23 // let crds: Api<CustomResourceDefinition> = Api::all(client);
24 // let crd = crds.get("widgets.example.com").await?;
25 // let schema = crd.spec.versions[0].schema.unwrap().open_api_v3_schema.unwrap();
26 // Here we mock that fetched OpenAPI v3 schema directly.
27 let schema = fetch_crd_schema();
28
29 let validator = Validator::new();
30
31 // A controller reconcile loop typically builds a *desired* object and applies
32 // it. We show two desired states: one valid, one that violates the CRD rules.
33 let desired_ok = json!({
34 "spec": { "replicas": 3, "image": "registry.example.com/widget:1.4.2" }
35 });
36 let desired_bad = json!({
37 "spec": { "replicas": 0, "image": "widget:latest" }
38 });
39
40 for (label, desired) in [("valid desired", &desired_ok), ("invalid desired", &desired_bad)] {
41 println!("=== reconcile: {label} ===");
42
43 // Client-side gate, run BEFORE touching the apiserver.
44 let errors = validator.validate(&schema, desired, None);
45
46 if errors.is_empty() {
47 println!(" validation passed -> apply to cluster");
48 // The real apply happens only on the happy path:
49 // let widgets: Api<Widget> = Api::namespaced(client, "default");
50 // widgets.patch("my-widget", &PatchParams::apply("my-controller"),
51 // &Patch::Apply(desired)).await?;
52 } else {
53 println!(" validation failed -> skip apply, record on status:");
54 for err in &errors {
55 println!(" [{}] {}", err.field_path, err.message);
56 }
57 // Instead of a doomed apply, the controller would surface the reason:
58 // widgets.patch_status("my-widget", &pp,
59 // &Patch::Merge(json!({"status": {"validationError": err.message}}))).await?;
60 }
61 println!();
62 }
63}18fn main() {
19 // Three rules, each failing a different way against the same object.
20 let schema = json!({
21 "type": "object",
22 "x-kubernetes-validations": [
23 // (1) the object simply violates the rule -> ValidationFailure
24 {"rule": "self.replicas >= 1", "message": "replicas must be at least 1"},
25 // (2) the rule references a field that isn't there -> EvaluationError
26 // (compiles fine, errors at runtime; the cel error is the cause)
27 {"rule": "self.maxSurge > 0", "message": "maxSurge must be positive"},
28 // (3) the rule uses a CEL macro cel 0.13 can't evaluate -> UnsupportedReference
29 {"rule": "self.zones.sortBy(z, z) == self.zones", "message": "zones must be sorted"}
30 ],
31 "properties": {
32 "replicas": {"type": "integer"},
33 "zones": {"type": "array", "items": {"type": "string"}}
34 }
35 });
36
37 let object = json!({"replicas": 0, "zones": ["b", "a"]});
38
39 let errors = Validator::new().validate(&schema, &object, None);
40
41 println!("{} validation error(s)\n", errors.len());
42 for err in &errors {
43 println!("rule: {}", err.rule);
44 println!(" kind: {:?}", err.kind);
45 println!(" message: {err}");
46
47 // Branch on the classification. `ErrorKind` is `#[non_exhaustive]`, so a
48 // wildcard arm is required.
49 match err.kind {
50 ErrorKind::ValidationFailure => {
51 println!(" -> the object violated the rule; reject it");
52 }
53 ErrorKind::EvaluationError => {
54 println!(" -> the rule failed at runtime; likely a malformed object");
55 }
56 ErrorKind::UnsupportedReference => {
57 println!(" -> coverage gap: this kube-cel build can't evaluate the rule");
58 }
59 _ => println!(" -> other ({:?})", err.kind),
60 }
61
62 // Walk the `source()` cause chain. Runtime failures carry the owned `cel`
63 // execution error as their cause; pure ValidationFailure has none.
64 let mut cause = err.source();
65 if cause.is_none() {
66 println!(" (no underlying cause)");
67 }
68 let mut depth = 1;
69 while let Some(e) = cause {
70 println!(" {:indent$}caused by: {e}", "", indent = depth * 2);
71 cause = e.source();
72 depth += 1;
73 }
74 println!();
75 }
76}8fn main() {
9 let schema = json!({
10 "type": "object",
11 "properties": {
12 "replicas": {
13 "type": "integer",
14 "default": 1,
15 "x-kubernetes-validations": [
16 {"rule": "self >= 0", "message": "replicas must be non-negative"}
17 ]
18 },
19 "strategy": {
20 "type": "string",
21 "default": "RollingUpdate"
22 }
23 },
24 "x-kubernetes-validations": [
25 {"rule": "apiGroup == 'apps'", "message": "only apps group allowed"},
26 {"rule": "kind == 'Deployment'", "message": "must be a Deployment"}
27 ]
28 });
29
30 // Object with missing fields (would have defaults in K8s)
31 let object = json!({"replicas": 3});
32
33 // 1. Apply defaults
34 let defaulted = apply_defaults(&schema, &object);
35 println!("=== Default Injection ===");
36 println!("Before: {object}");
37 println!("After: {defaulted}\n");
38
39 // 2. Validate with root context
40 let root_ctx = RootContext {
41 api_version: "apps/v1".into(),
42 api_group: "apps".into(),
43 kind: "Deployment".into(),
44 };
45
46 let validator = Validator::new();
47 let errors = validator.validate_with_context(&schema, &defaulted, None, Some(&root_ctx));
48
49 println!("=== Validation Results ===");
50 if errors.is_empty() {
51 println!("All rules passed!");
52 } else {
53 for e in &errors {
54 println!("[FAIL] {}: {}", e.field_path, e.message);
55 }
56 }
57
58 // 3. Demonstrate validate_with_defaults convenience method
59 // Uses a simpler schema without root-context variables (apiGroup/kind),
60 // since validate_with_defaults does not accept a RootContext.
61 println!("\n=== validate_with_defaults ===");
62 let simple_schema = json!({
63 "type": "object",
64 "properties": {
65 "replicas": {
66 "type": "integer",
67 "default": 1,
68 "x-kubernetes-validations": [
69 {"rule": "self >= 0", "message": "replicas must be non-negative"}
70 ]
71 },
72 "strategy": {
73 "type": "string",
74 "default": "RollingUpdate"
75 }
76 }
77 });
78 let sparse_object = json!({}); // no fields at all
79 let errors = validator.validate_with_defaults(&simple_schema, &sparse_object, None);
80 println!("Validating empty object with defaults applied:");
81 if errors.is_empty() {
82 println!("All rules passed (replicas defaulted to 1, strategy to RollingUpdate)");
83 } else {
84 for e in &errors {
85 println!("[FAIL] {}", e.message);
86 }
87 }
88}Sourcepub fn validate(
&self,
schema: &Value,
object: &Value,
old_object: Option<&Value>,
) -> Vec<ValidationError>
pub fn validate( &self, schema: &Value, object: &Value, old_object: Option<&Value>, ) -> Vec<ValidationError>
Validate an object against a CRD schema’s CEL validation rules.
Compiles rules on each call. For repeated validation against the same
schema, prefer compile_schema + validate_compiled.
Examples found in repository?
14fn main() {
15 let schema = json!({
16 "type": "object",
17 "properties": {
18 "spec": {
19 "type": "object",
20 "properties": {
21 "replicas": {"type": "integer"}
22 },
23 "x-kubernetes-validations": [{
24 "rule": "self.replicas <= 5",
25 // Static fallback, used if messageExpression is absent/non-string.
26 "message": "too many replicas",
27 // Dynamic message: embeds the actual value and the limit.
28 "messageExpression":
29 "'requested ' + string(self.replicas) + ' replicas, max allowed is 5'"
30 }]
31 }
32 }
33 });
34
35 let validator = Validator::new();
36
37 // Two invalid objects: the dynamic message differs per object.
38 for replicas in [8, 20] {
39 let object = json!({"spec": {"replicas": replicas}});
40 let errors = validator.validate(&schema, &object, None);
41 for err in &errors {
42 // Note the message reflects this object's value, not a fixed string.
43 println!("replicas={replicas}: {}", err.message);
44 }
45 }
46
47 // A valid object produces no errors (and so no message at all).
48 let ok = json!({"spec": {"replicas": 3}});
49 println!(
50 "replicas=3: {} error(s)",
51 validator.validate(&schema, &ok, None).len()
52 );
53}More examples
17fn main() {
18 let schema = json!({
19 "type": "object",
20 "properties": {
21 "storageClass": {"type": "string"},
22 "replicas": {"type": "integer"}
23 },
24 "x-kubernetes-validations": [
25 // Immutable field: may never change once set.
26 {
27 "rule": "self.storageClass == oldSelf.storageClass",
28 "message": "storageClass is immutable"
29 },
30 // Monotonic: replicas may only grow (no scale-down).
31 {
32 "rule": "self.replicas >= oldSelf.replicas",
33 "message": "replicas cannot be decreased"
34 }
35 ]
36 });
37
38 let validator = Validator::new();
39 let stored = json!({"storageClass": "fast-ssd", "replicas": 3});
40
41 // CREATE: no previous state, so transition rules are skipped entirely.
42 let created = json!({"storageClass": "fast-ssd", "replicas": 3});
43 report("CREATE (old = None)", validator.validate(&schema, &created, None));
44
45 // UPDATE that respects both rules: same storageClass, scaling up.
46 let scale_up = json!({"storageClass": "fast-ssd", "replicas": 5});
47 report(
48 "UPDATE scale up 3->5",
49 validator.validate(&schema, &scale_up, Some(&stored)),
50 );
51
52 // UPDATE that violates the monotonic rule: scaling down.
53 let scale_down = json!({"storageClass": "fast-ssd", "replicas": 1});
54 report(
55 "UPDATE scale down 3->1",
56 validator.validate(&schema, &scale_down, Some(&stored)),
57 );
58
59 // UPDATE that mutates an immutable field.
60 let restorage = json!({"storageClass": "cheap-hdd", "replicas": 3});
61 report(
62 "UPDATE change storageClass",
63 validator.validate(&schema, &restorage, Some(&stored)),
64 );
65}8fn main() {
9 let schema = json!({
10 "type": "object",
11 "properties": {
12 "spec": {
13 "type": "object",
14 "properties": {
15 "replicas": {
16 "type": "integer",
17 "x-kubernetes-validations": [
18 {"rule": "self >= 0", "message": "replicas must be non-negative"}
19 ]
20 }
21 },
22 "x-kubernetes-validations": [
23 {"rule": "self.replicas >= 1", "message": "at least one replica required"}
24 ]
25 }
26 }
27 });
28
29 let validator = Validator::new();
30
31 // Valid object
32 let valid = json!({"spec": {"replicas": 3}});
33 let errors = validator.validate(&schema, &valid, None);
34 println!("Valid object: {} errors", errors.len());
35
36 // Invalid object
37 let invalid = json!({"spec": {"replicas": -1}});
38 let errors = validator.validate(&schema, &invalid, None);
39 println!("\nInvalid object: {} errors", errors.len());
40 for err in &errors {
41 println!(" [{path}] {msg}", path = err.field_path, msg = err.message);
42 }
43
44 // Transition rule (update check)
45 let transition_schema = json!({
46 "type": "object",
47 "x-kubernetes-validations": [{
48 "rule": "self.replicas >= oldSelf.replicas",
49 "message": "cannot scale down"
50 }],
51 "properties": {
52 "replicas": {"type": "integer"}
53 }
54 });
55
56 let new_obj = json!({"replicas": 2});
57 let old_obj = json!({"replicas": 5});
58
59 // Create (no old object): transition rule skipped
60 let errors = validator.validate(&transition_schema, &new_obj, None);
61 println!("\nCreate (no old): {} errors", errors.len());
62
63 // Update (scale down): transition rule fires
64 let errors = validator.validate(&transition_schema, &new_obj, Some(&old_obj));
65 println!("Update (scale down): {} errors", errors.len());
66 for err in &errors {
67 println!(" {}", err);
68 }
69}19fn main() {
20 // In a real controller this comes from the cluster, e.g.:
21 // use kube::{Api, Client};
22 // use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
23 // let crds: Api<CustomResourceDefinition> = Api::all(client);
24 // let crd = crds.get("widgets.example.com").await?;
25 // let schema = crd.spec.versions[0].schema.unwrap().open_api_v3_schema.unwrap();
26 // Here we mock that fetched OpenAPI v3 schema directly.
27 let schema = fetch_crd_schema();
28
29 let validator = Validator::new();
30
31 // A controller reconcile loop typically builds a *desired* object and applies
32 // it. We show two desired states: one valid, one that violates the CRD rules.
33 let desired_ok = json!({
34 "spec": { "replicas": 3, "image": "registry.example.com/widget:1.4.2" }
35 });
36 let desired_bad = json!({
37 "spec": { "replicas": 0, "image": "widget:latest" }
38 });
39
40 for (label, desired) in [("valid desired", &desired_ok), ("invalid desired", &desired_bad)] {
41 println!("=== reconcile: {label} ===");
42
43 // Client-side gate, run BEFORE touching the apiserver.
44 let errors = validator.validate(&schema, desired, None);
45
46 if errors.is_empty() {
47 println!(" validation passed -> apply to cluster");
48 // The real apply happens only on the happy path:
49 // let widgets: Api<Widget> = Api::namespaced(client, "default");
50 // widgets.patch("my-widget", &PatchParams::apply("my-controller"),
51 // &Patch::Apply(desired)).await?;
52 } else {
53 println!(" validation failed -> skip apply, record on status:");
54 for err in &errors {
55 println!(" [{}] {}", err.field_path, err.message);
56 }
57 // Instead of a doomed apply, the controller would surface the reason:
58 // widgets.patch_status("my-widget", &pp,
59 // &Patch::Merge(json!({"status": {"validationError": err.message}}))).await?;
60 }
61 println!();
62 }
63}18fn main() {
19 // Three rules, each failing a different way against the same object.
20 let schema = json!({
21 "type": "object",
22 "x-kubernetes-validations": [
23 // (1) the object simply violates the rule -> ValidationFailure
24 {"rule": "self.replicas >= 1", "message": "replicas must be at least 1"},
25 // (2) the rule references a field that isn't there -> EvaluationError
26 // (compiles fine, errors at runtime; the cel error is the cause)
27 {"rule": "self.maxSurge > 0", "message": "maxSurge must be positive"},
28 // (3) the rule uses a CEL macro cel 0.13 can't evaluate -> UnsupportedReference
29 {"rule": "self.zones.sortBy(z, z) == self.zones", "message": "zones must be sorted"}
30 ],
31 "properties": {
32 "replicas": {"type": "integer"},
33 "zones": {"type": "array", "items": {"type": "string"}}
34 }
35 });
36
37 let object = json!({"replicas": 0, "zones": ["b", "a"]});
38
39 let errors = Validator::new().validate(&schema, &object, None);
40
41 println!("{} validation error(s)\n", errors.len());
42 for err in &errors {
43 println!("rule: {}", err.rule);
44 println!(" kind: {:?}", err.kind);
45 println!(" message: {err}");
46
47 // Branch on the classification. `ErrorKind` is `#[non_exhaustive]`, so a
48 // wildcard arm is required.
49 match err.kind {
50 ErrorKind::ValidationFailure => {
51 println!(" -> the object violated the rule; reject it");
52 }
53 ErrorKind::EvaluationError => {
54 println!(" -> the rule failed at runtime; likely a malformed object");
55 }
56 ErrorKind::UnsupportedReference => {
57 println!(" -> coverage gap: this kube-cel build can't evaluate the rule");
58 }
59 _ => println!(" -> other ({:?})", err.kind),
60 }
61
62 // Walk the `source()` cause chain. Runtime failures carry the owned `cel`
63 // execution error as their cause; pure ValidationFailure has none.
64 let mut cause = err.source();
65 if cause.is_none() {
66 println!(" (no underlying cause)");
67 }
68 let mut depth = 1;
69 while let Some(e) = cause {
70 println!(" {:indent$}caused by: {e}", "", indent = depth * 2);
71 cause = e.source();
72 depth += 1;
73 }
74 println!();
75 }
76}Sourcepub fn validate_with_context(
&self,
schema: &Value,
object: &Value,
old_object: Option<&Value>,
root_ctx: Option<&RootContext>,
) -> Vec<ValidationError>
pub fn validate_with_context( &self, schema: &Value, object: &Value, old_object: Option<&Value>, root_ctx: Option<&RootContext>, ) -> Vec<ValidationError>
Validate an object against a CRD schema’s CEL validation rules, with optional root context.
Like validate, but also binds apiVersion, apiGroup, and kind
as root-level CEL variables when a RootContext is provided.
Examples found in repository?
8fn main() {
9 let schema = json!({
10 "type": "object",
11 "properties": {
12 "replicas": {
13 "type": "integer",
14 "default": 1,
15 "x-kubernetes-validations": [
16 {"rule": "self >= 0", "message": "replicas must be non-negative"}
17 ]
18 },
19 "strategy": {
20 "type": "string",
21 "default": "RollingUpdate"
22 }
23 },
24 "x-kubernetes-validations": [
25 {"rule": "apiGroup == 'apps'", "message": "only apps group allowed"},
26 {"rule": "kind == 'Deployment'", "message": "must be a Deployment"}
27 ]
28 });
29
30 // Object with missing fields (would have defaults in K8s)
31 let object = json!({"replicas": 3});
32
33 // 1. Apply defaults
34 let defaulted = apply_defaults(&schema, &object);
35 println!("=== Default Injection ===");
36 println!("Before: {object}");
37 println!("After: {defaulted}\n");
38
39 // 2. Validate with root context
40 let root_ctx = RootContext {
41 api_version: "apps/v1".into(),
42 api_group: "apps".into(),
43 kind: "Deployment".into(),
44 };
45
46 let validator = Validator::new();
47 let errors = validator.validate_with_context(&schema, &defaulted, None, Some(&root_ctx));
48
49 println!("=== Validation Results ===");
50 if errors.is_empty() {
51 println!("All rules passed!");
52 } else {
53 for e in &errors {
54 println!("[FAIL] {}: {}", e.field_path, e.message);
55 }
56 }
57
58 // 3. Demonstrate validate_with_defaults convenience method
59 // Uses a simpler schema without root-context variables (apiGroup/kind),
60 // since validate_with_defaults does not accept a RootContext.
61 println!("\n=== validate_with_defaults ===");
62 let simple_schema = json!({
63 "type": "object",
64 "properties": {
65 "replicas": {
66 "type": "integer",
67 "default": 1,
68 "x-kubernetes-validations": [
69 {"rule": "self >= 0", "message": "replicas must be non-negative"}
70 ]
71 },
72 "strategy": {
73 "type": "string",
74 "default": "RollingUpdate"
75 }
76 }
77 });
78 let sparse_object = json!({}); // no fields at all
79 let errors = validator.validate_with_defaults(&simple_schema, &sparse_object, None);
80 println!("Validating empty object with defaults applied:");
81 if errors.is_empty() {
82 println!("All rules passed (replicas defaulted to 1, strategy to RollingUpdate)");
83 } else {
84 for e in &errors {
85 println!("[FAIL] {}", e.message);
86 }
87 }
88}Sourcepub fn validate_compiled(
&self,
compiled: &CompiledSchema,
object: &Value,
old_object: Option<&Value>,
) -> Vec<ValidationError>
pub fn validate_compiled( &self, compiled: &CompiledSchema, object: &Value, old_object: Option<&Value>, ) -> Vec<ValidationError>
Validate an object using a pre-compiled schema tree.
Use compile_schema to build the CompiledSchema, then call this
method for each object to validate — rules are compiled only once.
Sourcepub fn validate_compiled_with_context(
&self,
compiled: &CompiledSchema,
object: &Value,
old_object: Option<&Value>,
root_ctx: Option<&RootContext>,
) -> Vec<ValidationError>
pub fn validate_compiled_with_context( &self, compiled: &CompiledSchema, object: &Value, old_object: Option<&Value>, root_ctx: Option<&RootContext>, ) -> Vec<ValidationError>
Validate an object using a pre-compiled schema tree, with optional root context.
Like validate_compiled, but also binds apiVersion,
apiGroup, and kind as root-level CEL variables when a RootContext is provided.
Sourcepub fn validate_with_defaults(
&self,
schema: &Value,
object: &Value,
old_object: Option<&Value>,
) -> Vec<ValidationError>
pub fn validate_with_defaults( &self, schema: &Value, object: &Value, old_object: Option<&Value>, ) -> Vec<ValidationError>
Validate with schema defaults applied to the object first.
Equivalent to calling crate::validation::defaults::apply_defaults followed by validate.
Examples found in repository?
8fn main() {
9 let schema = json!({
10 "type": "object",
11 "properties": {
12 "replicas": {
13 "type": "integer",
14 "default": 1,
15 "x-kubernetes-validations": [
16 {"rule": "self >= 0", "message": "replicas must be non-negative"}
17 ]
18 },
19 "strategy": {
20 "type": "string",
21 "default": "RollingUpdate"
22 }
23 },
24 "x-kubernetes-validations": [
25 {"rule": "apiGroup == 'apps'", "message": "only apps group allowed"},
26 {"rule": "kind == 'Deployment'", "message": "must be a Deployment"}
27 ]
28 });
29
30 // Object with missing fields (would have defaults in K8s)
31 let object = json!({"replicas": 3});
32
33 // 1. Apply defaults
34 let defaulted = apply_defaults(&schema, &object);
35 println!("=== Default Injection ===");
36 println!("Before: {object}");
37 println!("After: {defaulted}\n");
38
39 // 2. Validate with root context
40 let root_ctx = RootContext {
41 api_version: "apps/v1".into(),
42 api_group: "apps".into(),
43 kind: "Deployment".into(),
44 };
45
46 let validator = Validator::new();
47 let errors = validator.validate_with_context(&schema, &defaulted, None, Some(&root_ctx));
48
49 println!("=== Validation Results ===");
50 if errors.is_empty() {
51 println!("All rules passed!");
52 } else {
53 for e in &errors {
54 println!("[FAIL] {}: {}", e.field_path, e.message);
55 }
56 }
57
58 // 3. Demonstrate validate_with_defaults convenience method
59 // Uses a simpler schema without root-context variables (apiGroup/kind),
60 // since validate_with_defaults does not accept a RootContext.
61 println!("\n=== validate_with_defaults ===");
62 let simple_schema = json!({
63 "type": "object",
64 "properties": {
65 "replicas": {
66 "type": "integer",
67 "default": 1,
68 "x-kubernetes-validations": [
69 {"rule": "self >= 0", "message": "replicas must be non-negative"}
70 ]
71 },
72 "strategy": {
73 "type": "string",
74 "default": "RollingUpdate"
75 }
76 }
77 });
78 let sparse_object = json!({}); // no fields at all
79 let errors = validator.validate_with_defaults(&simple_schema, &sparse_object, None);
80 println!("Validating empty object with defaults applied:");
81 if errors.is_empty() {
82 println!("All rules passed (replicas defaulted to 1, strategy to RollingUpdate)");
83 } else {
84 for e in &errors {
85 println!("[FAIL] {}", e.message);
86 }
87 }
88}Sourcepub fn validate_with_defaults_and_context(
&self,
schema: &Value,
object: &Value,
old_object: Option<&Value>,
root_ctx: Option<&RootContext>,
) -> Vec<ValidationError>
pub fn validate_with_defaults_and_context( &self, schema: &Value, object: &Value, old_object: Option<&Value>, root_ctx: Option<&RootContext>, ) -> Vec<ValidationError>
Validate with schema defaults applied and root context variables bound.
Combines crate::validation::defaults::apply_defaults with Self::validate_with_context.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Validator
impl !UnwindSafe for Validator
impl Freeze for Validator
impl Send for Validator
impl Sync for Validator
impl Unpin for Validator
impl UnsafeUnpin for Validator
Blanket Implementations§
Source§impl<T> AnyExt for T
impl<T> AnyExt for T
Source§fn downcast_ref<T>(this: &Self) -> Option<&T>where
T: Any,
fn downcast_ref<T>(this: &Self) -> Option<&T>where
T: Any,
T behind referenceSource§fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>where
T: Any,
fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>where
T: Any,
T behind mutable referenceSource§fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>where
T: Any,
fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>where
T: Any,
T behind Rc pointerSource§fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>where
T: Any,
fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>where
T: Any,
T behind Arc pointer