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