canic_host/deployment_truth/
plan.rs1use super::*;
2use crate::release_set::{configured_fleet_name, configured_fleet_roles};
3use std::path::PathBuf;
4
5#[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#[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 deployment_manifest_digest = config_sha256_assumption(&config, &mut unresolved_assumptions);
46 let artifact_manifest = collect_local_role_artifact_manifest(&LocalArtifactManifestRequest {
47 network: request.network.clone(),
48 workspace_root: request.workspace_root.clone(),
49 icp_root: request.icp_root.clone(),
50 config_path: Some(config),
51 });
52 unresolved_assumptions.extend(
53 artifact_manifest
54 .unresolved_artifacts
55 .into_iter()
56 .map(|gap| assumption(gap.key, gap.description)),
57 );
58
59 DeploymentPlanV1 {
60 schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
61 plan_id: format!("local:{}:{}:plan", request.network, request.deployment_name),
62 deployment_identity: DeploymentIdentityV1 {
63 deployment_name: request.deployment_name.clone(),
64 network: request.network.clone(),
65 root_principal: None,
66 authority_profile_hash: None,
67 role_topology_hash: None,
68 deployment_manifest_digest,
69 canonical_runtime_config_digest: None,
70 role_embedded_config_set_digest: None,
71 artifact_set_digest: None,
72 pool_identity_set_digest: None,
73 canic_version: Some(env!("CARGO_PKG_VERSION").to_string()),
74 ic_memory_version: None,
75 },
76 trust_domain: TrustDomainV1 {
77 root_trust_anchor: None,
78 migration_from: None,
79 },
80 fleet_template,
81 runtime_variant: request.runtime_variant.clone(),
82 authority_profile: AuthorityProfileV1 {
83 profile_id: format!(
84 "local:{}:{}:authority",
85 request.network, request.deployment_name
86 ),
87 expected_controllers: Vec::new(),
88 staging_controllers: Vec::new(),
89 emergency_controllers: Vec::new(),
90 },
91 role_artifacts: artifact_manifest
92 .role_artifacts
93 .into_iter()
94 .map(|mut artifact| {
95 artifact.build_profile.clone_from(&request.build_profile);
96 artifact
97 })
98 .collect(),
99 expected_canisters: roles
100 .into_iter()
101 .map(|role| ExpectedCanisterV1 {
102 role,
103 canister_id: None,
104 control_class: CanisterControlClassV1::DeploymentControlled,
105 })
106 .collect(),
107 expected_pool: Vec::new(),
108 expected_verifier_readiness: VerifierReadinessExpectationV1 {
109 required: false,
110 expected_role_epochs: Vec::new(),
111 },
112 unresolved_assumptions,
113 }
114}
115
116fn assumption(key: impl Into<String>, description: impl Into<String>) -> DeploymentAssumptionV1 {
117 DeploymentAssumptionV1 {
118 key: key.into(),
119 description: description.into(),
120 }
121}
122
123fn config_sha256_assumption(
124 path: &std::path::Path,
125 assumptions: &mut Vec<DeploymentAssumptionV1>,
126) -> Option<String> {
127 match file_sha256_hex(path) {
128 Ok(hash) => Some(hash),
129 Err(err) => {
130 assumptions.push(assumption(
131 "local_config.raw_sha256",
132 format!("could not hash config {}: {err}", path.display()),
133 ));
134 None
135 }
136 }
137}