Skip to main content

canic_host/deployment_truth/observe/inventory/
mod.rs

1use super::super::*;
2use super::artifacts::{
3    collect_observed_artifacts, observe_canonical_runtime_config_digest, observe_config_sha256,
4    observe_deployment_manifest_digest,
5};
6use super::config::observe_local_config_facts;
7use super::identity::{InventoryIdentityFacts, local_inventory_identity};
8use crate::fleet_catalog::{FleetCatalogError, read_fleet_catalog_entry_from_root};
9use crate::network::resolve_canonical_network_id_from_root;
10use std::path::PathBuf;
11use thiserror::Error as ThisError;
12
13///
14/// LocalInventoryRequest
15///
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct LocalInventoryRequest {
18    pub fleet_name: String,
19    pub environment: String,
20    pub artifact_environment: String,
21    pub workspace_root: PathBuf,
22    pub icp_root: PathBuf,
23    pub config_path: Option<PathBuf>,
24    pub observed_at: String,
25}
26
27///
28/// DeploymentTruthError
29///
30#[derive(Debug, ThisError)]
31pub enum DeploymentTruthError {
32    #[error("failed to read the canonical-network Fleet catalog: {0}")]
33    FleetCatalog(#[source] FleetCatalogError),
34}
35
36/// Collect read-only local deployment facts without querying or mutating IC state.
37pub fn collect_local_deployment_inventory(
38    request: &LocalInventoryRequest,
39) -> Result<DeploymentInventoryV1, DeploymentTruthError> {
40    let config = deployment_config_path(&request.workspace_root, request.config_path.as_deref());
41    let mut unresolved_observations = Vec::new();
42    let local_config_facts = observe_local_config_facts(&config, &mut unresolved_observations);
43
44    let installed_fleet = read_fleet_catalog_entry_from_root(
45        &request.icp_root,
46        &request.environment,
47        &request.fleet_name,
48    )
49    .map_err(DeploymentTruthError::FleetCatalog)?;
50    let canonical_network_id =
51        resolve_canonical_network_id_from_root(&request.icp_root, &request.environment)
52            .map_err(FleetCatalogError::from)
53            .map_err(DeploymentTruthError::FleetCatalog)?;
54    let raw_config_sha256 = observe_config_sha256(&config, &mut unresolved_observations);
55    let canonical_runtime_config_digest =
56        observe_canonical_runtime_config_digest(&config, &mut unresolved_observations);
57    let deployment_manifest_digest = observe_deployment_manifest_digest(
58        &request.icp_root,
59        &request.artifact_environment,
60        &mut unresolved_observations,
61    );
62    let observed_artifacts = collect_observed_artifacts(
63        &request.icp_root,
64        &request.artifact_environment,
65        &local_config_facts.roles,
66        &mut unresolved_observations,
67    );
68    // A terminal catalog row discovers the Coordinator only. It is not
69    // evidence for one legacy deployment root or Subnet Registry inventory.
70    let observed_canisters = Vec::new();
71    let observed_pool = Vec::new();
72    let observed_root = None;
73    let observed_identity = Some(local_inventory_identity(
74        request,
75        InventoryIdentityFacts {
76            canonical_network_id,
77            fleet_id: installed_fleet.as_ref().map(|fleet| fleet.fleet_id),
78            app: installed_fleet.as_ref().map_or_else(
79                || local_config_facts.app.clone(),
80                |fleet| fleet.app.to_string(),
81            ),
82            root_principal: None,
83            deployment_manifest_digest,
84            canonical_runtime_config_digest: canonical_runtime_config_digest.clone(),
85            observed_canisters: &observed_canisters,
86            observed_artifacts: &observed_artifacts,
87            observed_pool: &observed_pool,
88        },
89    ));
90
91    Ok(DeploymentInventoryV1 {
92        schema_version: DEPLOYMENT_TRUTH_SCHEMA_VERSION,
93        inventory_id: format!("local:{}:{}", request.environment, request.fleet_name),
94        observed_at: request.observed_at.clone(),
95        observed_identity,
96        observed_root,
97        local_config: LocalDeploymentConfigV1 {
98            config_path: Some(config.display().to_string()),
99            raw_config_sha256,
100            canonical_embedded_config_sha256: canonical_runtime_config_digest,
101        },
102        observed_canisters,
103        observed_pool,
104        observed_artifacts,
105        observed_verifier_readiness: VerifierReadinessObservationV1 {
106            status: ObservationStatusV1::NotObserved,
107            role_epochs: Vec::new(),
108        },
109        unresolved_observations,
110    })
111}