Skip to main content

gatehouse_runtime_protocol/lifecycle/journal/
resolution.rs

1//! Immutable pre-create plan and image evidence; no execution authority itself.
2use super::*;
3use crate::lifecycle::wire::Entry;
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(deny_unknown_fields)]
6pub struct CreatePlan {
7    pub name: String,
8    pub labels: Vec<Entry>,
9    pub environment: Vec<Entry>,
10    pub container_port: u16,
11}
12impl CreatePlan {
13    pub fn validate(&self, resolve: &Request) -> Result<()> {
14        resolve.validate()?;
15        if !matches!(resolve.operation, Operation::ResolveImage { .. })
16            || resolve.binding.generation != 1
17        {
18            return Err(Invalid("reservation requires initial image resolution"));
19        }
20        self.request(
21            resolve,
22            &format!("sha256:{}", "0".repeat(64)),
23            &resolve.binding.invocation,
24        )?
25        .validate()
26    }
27    fn request(&self, resolve: &Request, image_id: &str, invocation: &str) -> Result<Request> {
28        let mut request = resolve.clone();
29        request.operation = Operation::Create {
30            name: self.name.clone(),
31            image_id: image_id.into(),
32            labels: self.labels.clone(),
33            environment: self.environment.clone(),
34            container_port: self.container_port,
35        };
36        request.binding.generation = 2;
37        request.binding.invocation = invocation.into();
38        request.binding.operation_key = request.operation_key()?;
39        Ok(request)
40    }
41}
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(deny_unknown_fields)]
44pub(super) struct ResolutionProof {
45    pub request: Request,
46    pub plan: CreatePlan,
47    pub response: Response,
48}
49impl ResolutionProof {
50    pub fn create(&self, invocation: &str) -> Result<Request> {
51        self.plan.validate(&self.request)?;
52        self.response.validate(&self.request)?;
53        let Outcome::Image { image_id, .. } = &self.response.outcome else {
54            return Err(Invalid("image resolution not proven"));
55        };
56        if invocation == self.request.binding.invocation {
57            return Err(Invalid("create invocation must be fresh"));
58        }
59        let create = self.plan.request(&self.request, image_id, invocation)?;
60        create.validate()?;
61        Ok(create)
62    }
63    pub fn validate_create(&self, create: &Request) -> Result<()> {
64        if self.create(&create.binding.invocation)? != *create {
65            return Err(Invalid("create differs from reserved plan/image evidence"));
66        }
67        Ok(())
68    }
69}