Skip to main content

greentic_deployer/env_packs/
render.rs

1//! Declarative manifest rendering seam (plan §6 step 10).
2//!
3//! [`ManifestRenderer`] is the deployer-side contract behind
4//! `gtc op env render`: it turns an [`Environment`] (plus optional wizard
5//! answers) into the ordered list of declarative manifest documents the
6//! deployer would apply, without applying anything. This is what lets an
7//! operator choose direct apply, GitOps repository handoff, or rendered-
8//! manifest handoff — the rendered artifact and the applied resources come
9//! from the same functions.
10//!
11//! The trait deliberately sits NEXT TO [`Deployer`](super::deployer::Deployer)
12//! rather than on it: rendering only makes sense for deployers whose
13//! desired state is expressible as declarative documents (K8s). Imperative
14//! deployers (local-process, AWS-ECS) simply don't implement it — their
15//! handlers return `None` from
16//! [`EnvPackHandler::as_manifest_renderer`](super::slot::EnvPackHandler::as_manifest_renderer)
17//! and `op env render` reports the kind as non-renderable.
18
19use greentic_deploy_spec::Environment;
20use serde_json::Value;
21
22/// Renders an environment's full declarative desired state.
23///
24/// Contract:
25/// - **Pure and deterministic** — same `(env, answers)` pair, same
26///   documents, same order. No I/O, no provider calls, no clock.
27/// - **Apply order** — consumers may feed the list to `kubectl apply -f`
28///   (or commit it to a GitOps repo) as-is; dependencies come before
29///   dependents (e.g. Namespace before namespaced objects).
30/// - Each [`Value`] is one manifest document following the K8s object
31///   convention (`apiVersion` / `kind` / `metadata.name`).
32/// - The set covers environment-level objects AND per-revision workload
33///   objects for revisions whose persisted lifecycle implies presence in
34///   the desired state — the exact lifecycle policy is the impl's to
35///   define and document.
36pub trait ManifestRenderer: Send + Sync {
37    /// Render the env's declarative desired state, in apply order.
38    ///
39    /// `answers` is the deployer binding's recorded wizard answers as a
40    /// flat JSON object keyed by wizard question id (the ecosystem's
41    /// qa-spec answers convention). `None` when the binding records no
42    /// answers — the impl must fall back to sandbox defaults.
43    fn render_environment(
44        &self,
45        env: &Environment,
46        answers: Option<&serde_json::Value>,
47    ) -> Result<Vec<Value>, RenderError>;
48}
49
50/// Errors from [`ManifestRenderer::render_environment`].
51#[derive(Debug, thiserror::Error)]
52pub enum RenderError {
53    /// The binding's recorded answers are malformed or fail validation.
54    #[error("invalid binding answers: {0}")]
55    InvalidAnswers(String),
56}