Skip to main content

canic_host/install_root/
mod.rs

1use crate::canister_build::CanisterBuildProfile;
2use crate::deployment_truth::{
3    ArtifactPromotionPlanV1, DeploymentExecutorCapabilityV1, DeploymentPlanV1,
4};
5use crate::release_set::{configured_fleet_name, icp_root, workspace_root};
6use config_selection::resolve_install_config_path;
7use std::{
8    path::{Path, PathBuf},
9    time::{Instant, SystemTime, UNIX_EPOCH},
10};
11
12mod activation;
13mod artifact_promotion;
14mod build_environment;
15mod build_targets;
16mod commands;
17mod config_selection;
18mod current_execution;
19mod deployment_registration;
20mod deployment_truth_gate;
21mod execution_preflight;
22mod install_state;
23mod operations;
24mod output;
25mod phase_receipts;
26mod plan_artifacts;
27mod preparation;
28mod readiness;
29mod receipt_io;
30mod root_canister;
31mod root_cycles;
32mod root_verification;
33mod staging;
34mod state;
35mod timing;
36mod truth_check;
37
38use activation::run_root_activation_phases;
39use artifact_promotion::write_artifact_promotion_execution_receipt_for_install;
40use build_environment::BuildEnvGuard;
41#[cfg(test)]
42use commands::{
43    add_create_root_target, add_icp_environment_target, icp_canister_command_in_network,
44    is_missing_canister_id_error, parse_created_canister_id,
45};
46pub use config_selection::{
47    current_canic_project_root, discover_canic_config_choices, discover_canic_project_root_from,
48    discover_project_canic_config_choices, project_fleet_roots,
49};
50use current_execution::current_install_execution_context;
51#[cfg(test)]
52use current_execution::current_install_executor_missing_capabilities;
53pub use deployment_registration::{
54    RegisterDeploymentStateOptions, VerifyDeploymentRootOptions, register_deployment_state,
55    verify_registered_deployment_root,
56};
57#[cfg(test)]
58use deployment_truth_gate::{
59    enforce_install_deployment_truth_gate, install_deployment_truth_gate_lines,
60    install_deployment_truth_gate_receipt,
61};
62#[cfg(test)]
63use execution_preflight::write_current_install_execution_preflight_receipt;
64use install_state::{build_install_state, write_install_state_with_deployment_truth_receipt};
65#[cfg(test)]
66use operations::EmitRootManifestOperation;
67#[cfg(test)]
68use operations::{
69    BuildInstallTargetsOperation, EnsureRootCyclesOperation, InstallRootWasmOperation,
70    ResolveRootCanisterOperation, ResumeBootstrapOperation, WaitRootReadyOperation,
71};
72#[cfg(test)]
73use output::render_install_timing_summary;
74use output::{print_install_result_summary, print_install_timing_summary};
75use phase_receipts::InstallReceiptScope;
76#[cfg(test)]
77use phase_receipts::install_deployment_truth_phase_receipt;
78#[cfg(test)]
79use phase_receipts::{CompletedInstallPhase, write_completed_install_phase_receipt};
80use plan_artifacts::emit_manifest_with_deployment_truth_receipt;
81#[cfg(test)]
82use plan_artifacts::validate_plan_artifacts_with_phase;
83use preparation::prepare_install_deployment_truth;
84pub use receipt_io::latest_deployment_truth_receipt_path_from_root;
85#[cfg(test)]
86use receipt_io::write_install_deployment_truth_receipt;
87#[cfg(test)]
88use root_cycles::add_local_root_create_cycles_arg;
89#[cfg(test)]
90use root_verification::write_verified_root_state_if_unchanged;
91#[cfg(test)]
92use staging::StageReleaseSetOperation;
93#[cfg(test)]
94use staging::current_install_staging_evidence;
95use state::validate_state_name;
96#[cfg(test)]
97use state::{INSTALL_STATE_SCHEMA_VERSION, write_install_state};
98pub use state::{
99    InstallState, RootVerificationStatus, read_named_deployment_install_state,
100    read_named_deployment_install_state_from_root,
101};
102#[cfg(test)]
103use state::{deployment_install_state_path, read_deployment_install_state};
104#[cfg(test)]
105use timing::InstallTimingSummary;
106use timing::InstallTimingSummary as CurrentInstallTimingSummary;
107#[cfg(test)]
108use truth_check::current_install_deployment_truth_check_at;
109use truth_check::validate_expected_fleet_name;
110pub use truth_check::{check_install_deployment_truth, check_install_execution_preflight};
111
112#[cfg(test)]
113mod tests;
114
115#[cfg(test)]
116use commands::{parse_canister_id_json, root_init_args};
117#[cfg(test)]
118use config_selection::config_selection_error;
119#[cfg(test)]
120use readiness::parse_bootstrap_status_value;
121#[cfg(test)]
122use receipt_io::install_deployment_truth_receipt_path;
123#[cfg(test)]
124use state::legacy_fleet_install_state_path;
125
126///
127/// InstallRootOptions
128///
129
130#[derive(Clone, Debug)]
131pub struct InstallRootOptions {
132    pub root_canister: String,
133    pub root_build_target: String,
134    pub network: String,
135    pub deployment_name: Option<String>,
136    pub icp_root: Option<PathBuf>,
137    pub build_profile: Option<CanisterBuildProfile>,
138    pub ready_timeout_seconds: u64,
139    pub config_path: Option<String>,
140    pub expected_fleet: Option<String>,
141    pub interactive_config_selection: bool,
142    pub deployment_plan_override: Option<DeploymentPlanV1>,
143    pub artifact_promotion_plan_override: Option<ArtifactPromotionPlanV1>,
144}
145
146const CURRENT_INSTALL_REQUIRED_CAPABILITIES: &[DeploymentExecutorCapabilityV1] = &[
147    DeploymentExecutorCapabilityV1::CreateCanister,
148    DeploymentExecutorCapabilityV1::InstallCode,
149    DeploymentExecutorCapabilityV1::Call,
150    DeploymentExecutorCapabilityV1::Query,
151    DeploymentExecutorCapabilityV1::StageArtifact,
152];
153
154/// Discover installable Canic config choices under the current workspace.
155pub fn discover_current_canic_config_choices() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
156    let project_root = current_canic_project_root()?;
157    let choices = config_selection::discover_workspace_canic_config_choices(&project_root)?;
158    if !choices.is_empty() {
159        return Ok(choices);
160    }
161
162    if let Ok(icp_root) = icp_root()
163        && icp_root != project_root
164    {
165        return config_selection::discover_workspace_canic_config_choices(&icp_root);
166    }
167
168    Ok(choices)
169}
170
171// Execute the local thin-root install flow against an already running replica.
172pub fn install_root(options: InstallRootOptions) -> Result<(), Box<dyn std::error::Error>> {
173    let workspace_root = workspace_root()?;
174    let icp_root = match &options.icp_root {
175        Some(path) => path.canonicalize()?,
176        None => icp_root()?,
177    };
178    let config_path = resolve_install_config_path(
179        &icp_root,
180        options.config_path.as_deref(),
181        options.interactive_config_selection,
182    )?;
183    let _install_env = BuildEnvGuard::apply(&options.network, &config_path, &icp_root);
184    let (fleet_name, deployment_name) = resolve_install_identity(&options, &config_path)?;
185    let total_started_at = Instant::now();
186    let mut timings = CurrentInstallTimingSummary::default();
187    let network = options.network.as_str();
188    let execution_context = current_install_execution_context(&workspace_root, &icp_root, network);
189
190    println!("Installing deployment {deployment_name}");
191    println!("Fleet template {fleet_name}");
192    println!();
193    let prepared = prepare_install_deployment_truth(
194        &options,
195        &workspace_root,
196        &icp_root,
197        &config_path,
198        &deployment_name,
199        &execution_context,
200    )?;
201    timings.create_canisters = prepared.timings.create_canisters;
202    timings.build_all = prepared.timings.build_all;
203
204    let (manifest_path, emit_manifest_duration) = emit_manifest_with_deployment_truth_receipt(
205        &workspace_root,
206        &icp_root,
207        &options,
208        &config_path,
209        &deployment_name,
210        &prepared.deployment_truth_check,
211        &execution_context,
212    )?;
213    timings.emit_manifest = emit_manifest_duration;
214    let activation_timings = run_root_activation_phases(
215        InstallReceiptScope {
216            icp_root: &icp_root,
217            network,
218            deployment_name: &deployment_name,
219            check: &prepared.deployment_truth_check,
220            execution_context: Some(&execution_context),
221        },
222        &options,
223        &prepared.root_canister_id,
224        &manifest_path,
225        total_started_at,
226    )?;
227    timings.install_root = activation_timings.install_root;
228    timings.fund_root = activation_timings.fund_root;
229    timings.stage_release_set = activation_timings.stage_release_set;
230    timings.resume_bootstrap = activation_timings.resume_bootstrap;
231    timings.wait_ready = activation_timings.wait_ready;
232    timings.finalize_root_funding = activation_timings.finalize_root_funding;
233
234    print_install_timing_summary(&timings, total_started_at.elapsed());
235    let state = build_install_state(
236        &options,
237        &workspace_root,
238        &icp_root,
239        &config_path,
240        &manifest_path,
241        (&deployment_name, &fleet_name),
242        &prepared.root_canister_id,
243    )?;
244    let state_path = write_install_state_with_deployment_truth_receipt(
245        InstallReceiptScope {
246            icp_root: &icp_root,
247            network,
248            deployment_name: &deployment_name,
249            check: &prepared.deployment_truth_check,
250            execution_context: Some(&execution_context),
251        },
252        &options.network,
253        &state,
254    )?;
255    write_artifact_promotion_execution_receipt_for_install(
256        &options,
257        &icp_root,
258        network,
259        &deployment_name,
260        &prepared.deployment_truth_check,
261        &execution_context,
262    )?;
263    print_install_result_summary(
264        &options.network,
265        &state.deployment_name,
266        &state.fleet_template,
267        &state_path,
268    );
269    Ok(())
270}
271
272fn resolve_install_identity(
273    options: &InstallRootOptions,
274    config_path: &Path,
275) -> Result<(String, String), Box<dyn std::error::Error>> {
276    let fleet_name = configured_fleet_name(config_path)?;
277    validate_expected_fleet_name(options.expected_fleet.as_deref(), &fleet_name, config_path)?;
278    validate_state_name(&fleet_name)?;
279    let deployment_name = options
280        .deployment_name
281        .clone()
282        .unwrap_or_else(|| fleet_name.clone());
283    validate_state_name(&deployment_name)?;
284    Ok((fleet_name, deployment_name))
285}
286
287// Read the current host clock as a unix timestamp for install state.
288fn current_unix_secs() -> Result<u64, Box<dyn std::error::Error>> {
289    Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
290}
291
292fn current_unix_timestamp_label() -> Result<String, Box<dyn std::error::Error>> {
293    Ok(format!("unix:{}", current_unix_secs()?))
294}