Skip to main content

Validator

Struct Validator 

Source
pub struct Validator { /* private fields */ }
Available on crate feature 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

Source

pub fn new() -> Self

Create a new Validator with all K8s CEL functions pre-registered.

Examples found in repository?
examples/message_expression.rs (line 35)
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
Hide additional examples
examples/transition_rules.rs (line 38)
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}
examples/validate_crd.rs (line 29)
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}
examples/kube_workflow.rs (line 29)
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}
examples/error_chain.rs (line 39)
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}
examples/defaults_and_context.rs (line 46)
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}
Source

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?
examples/message_expression.rs (line 40)
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
Hide additional examples
examples/transition_rules.rs (line 43)
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}
examples/validate_crd.rs (line 33)
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}
examples/kube_workflow.rs (line 44)
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}
examples/error_chain.rs (line 39)
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}
Source

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?
examples/defaults_and_context.rs (line 47)
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}
Source

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.

Source

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.

Source

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?
examples/defaults_and_context.rs (line 79)
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}
Source

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§

Source§

impl Debug for Validator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Validator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> AnyExt for T
where T: Any + ?Sized,

Source§

fn downcast_ref<T>(this: &Self) -> Option<&T>
where T: Any,

Attempts to downcast this to T behind reference
Source§

fn downcast_mut<T>(this: &mut Self) -> Option<&mut T>
where T: Any,

Attempts to downcast this to T behind mutable reference
Source§

fn downcast_rc<T>(this: Rc<Self>) -> Result<Rc<T>, Rc<Self>>
where T: Any,

Attempts to downcast this to T behind Rc pointer
Source§

fn downcast_arc<T>(this: Arc<Self>) -> Result<Arc<T>, Arc<Self>>
where T: Any,

Attempts to downcast this to T behind Arc pointer
Source§

fn downcast_box<T>(this: Box<Self>) -> Result<Box<T>, Box<Self>>
where T: Any,

Attempts to downcast this to T behind Box pointer
Source§

fn downcast_move<T>(this: Self) -> Option<T>
where T: Any, Self: Sized,

Attempts to downcast owned Self to T, useful only in generic context as a workaround for specialization
Source§

impl<T> AsDebug for T
where T: Debug,

Source§

fn as_debug(&self) -> &dyn Debug

Returns self as a &dyn Debug trait object.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T, X> CoerceTo<T> for X
where T: CoerceFrom<X> + ?Sized,

Source§

fn coerce_rc_to(self: Rc<X>) -> Rc<T>

Source§

fn coerce_box_to(self: Box<X>) -> Box<T>

Source§

fn coerce_ref_to(&self) -> &T

Source§

fn coerce_mut_to(&mut self) -> &mut T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.