pub(in crate::fleet_ensure) mod adoption;
pub(in crate::fleet_ensure) mod source;
pub(in crate::fleet_ensure) mod terminal;
use super::{EnsureStateError, FleetReinstallObservation};
use crate::fleet_ensure::model::{
DesiredCanister, DesiredCanisterKind, DesiredFleet, DesiredPresence, FleetReinstallRecord,
FleetReinstallSourceRecord, ReviewedDesiredFleetRecord,
};
use std::{collections::BTreeMap, path::Path};
pub(in crate::fleet_ensure) fn funding_seal(
source: &FleetReinstallSourceRecord,
binding: &crate::fleet_ensure::model::RootManagementBinding,
kind: DesiredCanisterKind,
) -> Option<crate::fleet_ensure::model::EnsureAction> {
let protocol = source.reviewed_desired.desired().protocol.as_ref()?;
let candid = match kind {
DesiredCanisterKind::Root => &protocol.root_candid,
DesiredCanisterKind::Coordinator => &protocol.coordinator_candid,
_ => return None,
};
Some(crate::fleet_ensure::model::EnsureAction::SealAuthority {
authority_kind: kind,
candid: candid.clone(),
candid_sha256: source.candid_sha256_by_path.get(candid)?.clone(),
name: binding.name.clone(),
principal: binding.principal.clone(),
})
}
pub(in crate::fleet_ensure) fn target_artifacts_sha256(
root: &Path,
desired: &DesiredFleet,
) -> Result<String, EnsureStateError> {
let artifacts = super::resolve_desired_artifacts(root, desired)?;
let steps = artifacts
.protocol_by_step
.iter()
.map(|(name, step)| {
(
name,
&step.candid_sha256,
&step.command_args_sha256,
&step.expected_status_sha256,
&step.status_args_sha256,
)
})
.collect::<Vec<_>>();
let continuation = artifacts.continuation.as_ref().map(|authority| {
(
&authority.app_config_sha256,
&authority.application_artifact_union_sha256,
&authority.coordinator_candid_sha256,
&authority.root_candid_sha256,
&authority.store_candid_sha256,
)
});
let bytes = crate::fleet_ensure::json::to_vec(&(
continuation,
artifacts.drain_candid_sha256_by_canister,
artifacts.init_arg_sha256_by_canister,
artifacts.init_candid_sha256_by_canister,
artifacts.wasm_sha256_by_canister,
steps,
))
.map_err(|source| EnsureStateError::Decode {
path: root.to_path_buf(),
source,
})?;
Ok(canic_core::cdk::utils::hash::sha256_hex(&bytes))
}
pub(in crate::fleet_ensure) fn capture_source(
root: &Path,
desired: &DesiredFleet,
) -> Result<FleetReinstallSourceRecord, EnsureStateError> {
let mut wasm_sha256_by_canister = BTreeMap::new();
for canister in &desired.canisters {
if let Some(path) = &canister.wasm {
wasm_sha256_by_canister
.insert(canister.name.clone(), super::artifact_sha256(root, path)?);
}
}
Ok(FleetReinstallSourceRecord {
terminal_retirement: None,
reviewed_desired: ReviewedDesiredFleetRecord::capture(desired),
wasm_sha256_by_canister,
candid_sha256_by_path: candid_hashes(root, desired)?,
})
}
pub(in crate::fleet_ensure) fn refresh_source(
root: &Path,
source: &FleetReinstallSourceRecord,
) -> Result<FleetReinstallSourceRecord, EnsureStateError> {
let mut refreshed = capture_source(root, source.reviewed_desired.desired())?;
refreshed
.terminal_retirement
.clone_from(&source.terminal_retirement);
Ok(refreshed)
}
pub(in crate::fleet_ensure) fn candid_hashes(
root: &Path,
desired: &DesiredFleet,
) -> Result<BTreeMap<String, String>, EnsureStateError> {
let mut hashes = BTreeMap::new();
if let Some(protocol) = &desired.protocol {
for path in [
&protocol.coordinator_candid,
&protocol.root_candid,
&protocol.store_candid,
] {
hashes.insert(path.clone(), super::artifact_sha256(root, path)?);
}
}
Ok(hashes)
}
pub(in crate::fleet_ensure) fn capture_imports(
desired: &DesiredFleet,
inventory: &FleetReinstallObservation,
) -> Option<DesiredFleet> {
let mut retained = desired.clone();
for canister in &mut retained.canisters {
if let Some(authority) = inventory.authorities.get(&canister.name) {
canister.principal = Some(authority.live.principal.clone());
} else {
canister.principal = Some(
inventory
.observation
.canisters
.get(&canister.name)?
.as_ref()?
.principal
.clone(),
);
}
}
let bootstrap = retained.bootstrap.as_mut()?;
bootstrap.fresh_estate = false;
for root in &mut bootstrap.roots {
root.canister_pool_imports.clear();
for asset in inventory
.assets
.iter()
.filter(|asset| asset.root == root.root)
{
let name = if let Some(configured) = retained
.canisters
.iter()
.find(|c| c.principal.as_deref() == Some(&asset.principal))
{
if configured.kind != DesiredCanisterKind::Pool {
return None;
}
configured.name.clone()
} else {
let name = format!("reinstall-pool-{}", asset.principal);
if retained.canisters.iter().any(|c| c.name == name) {
return None;
}
let cycles = root.limits.canister_pool.canister_cycles.to_string();
retained.canisters.push(DesiredCanister {
canic_init: None,
controller_canisters: vec![root.root.clone()],
controllers: Vec::new(),
drain: None,
initial_cycles: cycles.clone(),
init_arg: None,
init_candid: None,
kind: DesiredCanisterKind::Pool,
minimum_cycles: cycles,
name: name.clone(),
parent: Some(root.root.clone()),
presence: DesiredPresence::Present,
principal: Some(asset.principal.clone()),
protocol_binding: None,
replace: false,
subnet: asset.subnet.clone(),
wasm: None,
});
name
};
root.canister_pool_imports.push(name);
}
root.canister_pool_imports.sort();
}
Some(retained)
}
pub(in crate::fleet_ensure) fn capture_intent(
preparation: &FleetReinstallRecord,
inventory: &FleetReinstallObservation,
) -> FleetReinstallRecord {
let mut intent = preparation.clone();
intent.assets.clone_from(&inventory.assets);
intent
}
pub(in crate::fleet_ensure) fn capture_after_activation_reset(
intent: &FleetReinstallRecord,
inventory: &FleetReinstallObservation,
) -> Option<FleetReinstallRecord> {
let mut current = intent.clone();
current.authorities = inventory
.authorities
.values()
.map(authority_binding)
.collect::<Option<Vec<_>>>()?;
current.assets.clone_from(&inventory.assets);
Some(current)
}
pub(in crate::fleet_ensure) fn authority_binding(
observed: &crate::fleet_ensure::model::RootManagementCanisterObservation,
) -> Option<crate::fleet_ensure::model::RootManagementBinding> {
let mut controllers = observed.live.controllers.clone();
controllers.sort();
Some(crate::fleet_ensure::model::RootManagementBinding {
controllers,
module_sha256: observed.live.module_sha256.clone()?,
name: observed.name.clone(),
principal: observed.live.principal.clone(),
subnet: observed.subnet.clone(),
})
}
pub(in crate::fleet_ensure) fn terminal_authority(
plan: &crate::fleet_ensure::model::FleetEnsurePlan,
binding: &crate::fleet_ensure::model::RootManagementBinding,
) -> crate::fleet_ensure::model::RootManagementBinding {
let mut expected = binding.clone();
for action in plan.canisters.iter().flat_map(|canister| &canister.actions) {
if let crate::fleet_ensure::model::EnsureAction::Install {
name, wasm_sha256, ..
} = action
&& name == &binding.name
{
expected.module_sha256.clone_from(wasm_sha256);
}
}
expected
}