Skip to main content

lenso_app_authoring/host_authoring/
policy.rs

1use anyhow::{Context, bail};
2use lenso_app_plan::authoring::PluginDescriptor;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6/// One exact independently installable release/implementation admitted by the Host.
7#[derive(Clone, Debug, Deserialize, Serialize)]
8#[serde(deny_unknown_fields)]
9pub struct AdmittedRelease {
10    pub descriptor: PluginDescriptor,
11    pub manifest_digest: String,
12}
13
14/// Host-owned admission and effective-configuration limits for an explicit Slot.
15#[derive(Clone, Debug, Deserialize, Serialize)]
16#[serde(deny_unknown_fields)]
17pub struct SlotAdmission {
18    pub slot: String,
19    pub max_instances: usize,
20    pub releases: Vec<AdmittedRelease>,
21    pub configuration_schema: Option<Value>,
22}
23
24/// A local, bounded JSON Schema profile. No retrievers, regexes, or format callbacks.
25pub(super) fn compile_ceiling(schema: &Value) -> anyhow::Result<jsonschema::Validator> {
26    check_schema(schema, 0, &mut 0)?;
27    jsonschema::draft202012::options()
28        .build(schema)
29        .map_err(|_| anyhow::anyhow!("invalid Host configuration ceiling schema"))
30}
31
32fn check_schema(schema: &Value, depth: usize, count: &mut usize) -> anyhow::Result<()> {
33    *count += 1;
34    if depth > 64 || *count > 4096 {
35        bail!("Host configuration schema exceeds depth/node limits");
36    }
37    if schema.is_boolean() {
38        return Ok(());
39    }
40    let object = schema
41        .as_object()
42        .context("Host configuration schema must be an object or boolean")?;
43    for (key, value) in object {
44        match key.as_str() {
45            "properties" => {
46                for child in value
47                    .as_object()
48                    .context("schema properties must be an object")?
49                    .values()
50                {
51                    check_schema(child, depth + 1, count)?;
52                }
53            }
54            "items" | "additionalProperties" | "not" | "if" | "then" | "else" => {
55                check_schema(value, depth + 1, count)?;
56            }
57            "allOf" | "anyOf" | "oneOf" => {
58                for child in value
59                    .as_array()
60                    .context("schema combinator must be an array")?
61                {
62                    check_schema(child, depth + 1, count)?;
63                }
64            }
65            "type" | "const" | "enum" | "required" | "minimum" | "maximum" | "minItems"
66            | "maxItems" | "uniqueItems" | "minLength" | "maxLength" | "title" | "description"
67            | "$comment" => {}
68            _ => bail!("unsupported Host configuration schema keyword `{key}`"),
69        }
70    }
71    Ok(())
72}