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