Skip to main content

canic_host/deployment_truth/
plan.rs

1use super::*;
2use crate::release_set::{configured_fleet_name, configured_fleet_roles};
3use std::path::PathBuf;
4
5///
6/// LocalDeploymentPlanRequest
7///
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct LocalDeploymentPlanRequest {
10    pub deployment_name: String,
11    pub network: String,
12    pub workspace_root: PathBuf,
13    pub icp_root: PathBuf,
14    pub config_path: Option<PathBuf>,
15    pub runtime_variant: String,
16    pub build_profile: String,
17}
18
19/// Build a local deployment plan from resolved host config and local artifact
20/// observations without querying or mutating IC state.
21#[must_use]
22pub fn build_local_deployment_plan(request: &LocalDeploymentPlanRequest) -> DeploymentPlanV1 {
23    let config = deployment_config_path(&request.workspace_root, request.config_path.as_deref());
24    let mut unresolved_assumptions = Vec::new();
25    let fleet_template = configured_fleet_name(&config).unwrap_or_else(|err| {
26        unresolved_assumptions.push(assumption(
27            "local_config.fleet_name",
28            format!(
29                "could not resolve fleet template name from {}: {err}",
30                config.display()
31            ),
32        ));
33        request.deployment_name.clone()
34    });
35    let roles = configured_fleet_roles(&config).unwrap_or_else(|err| {
36        unresolved_assumptions.push(assumption(
37            "local_config.roles",
38            format!(
39                "could not resolve configured roles from {}: {err}",
40                config.display()
41            ),
42        ));
43        Vec::new()
44    });
45    let artifact_manifest = collect_local_role_artifact_manifest(&LocalArtifactManifestRequest {
46        network: request.network.clone(),
47        workspace_root: request.workspace_root.clone(),
48        icp_root: request.icp_root.clone(),
49        config_path: Some(config),
50    });
51    unresolved_assumptions.extend(
52        artifact_manifest
53            .unresolved_artifacts
54            .into_iter()
55            .map(|gap| assumption(gap.key, gap.description)),
56    );
57
58    DeploymentPlanV1 {
59        schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
60        plan_id: format!("local:{}:{}:plan", request.network, request.deployment_name),
61        deployment_identity: DeploymentIdentityV1 {
62            deployment_name: request.deployment_name.clone(),
63            network: request.network.clone(),
64            root_principal: None,
65            authority_profile_hash: None,
66            role_topology_hash: None,
67            deployment_manifest_digest: None,
68            canonical_runtime_config_digest: None,
69            role_embedded_config_set_digest: None,
70            artifact_set_digest: None,
71            pool_identity_set_digest: None,
72            canic_version: Some(env!("CARGO_PKG_VERSION").to_string()),
73            ic_memory_version: None,
74        },
75        trust_domain: TrustDomainV1 {
76            root_trust_anchor: None,
77            migration_from: None,
78        },
79        fleet_template,
80        runtime_variant: request.runtime_variant.clone(),
81        authority_profile: AuthorityProfileV1 {
82            profile_id: format!(
83                "local:{}:{}:authority",
84                request.network, request.deployment_name
85            ),
86            expected_controllers: Vec::new(),
87            staging_controllers: Vec::new(),
88            emergency_controllers: Vec::new(),
89        },
90        role_artifacts: artifact_manifest
91            .role_artifacts
92            .into_iter()
93            .map(|mut artifact| {
94                artifact.build_profile.clone_from(&request.build_profile);
95                artifact
96            })
97            .collect(),
98        expected_canisters: roles
99            .into_iter()
100            .map(|role| ExpectedCanisterV1 {
101                role,
102                canister_id: None,
103                control_class: CanisterControlClassV1::DeploymentControlled,
104            })
105            .collect(),
106        expected_pool: Vec::new(),
107        expected_verifier_readiness: VerifierReadinessExpectationV1 {
108            required: false,
109            expected_role_epochs: Vec::new(),
110        },
111        unresolved_assumptions,
112    }
113}
114
115fn assumption(key: impl Into<String>, description: impl Into<String>) -> DeploymentAssumptionV1 {
116    DeploymentAssumptionV1 {
117        key: key.into(),
118        description: description.into(),
119    }
120}