use super::{
LiveNnsRegistryRefreshSource, SubnetCatalogCacheRequest, SubnetCatalogHostError,
SubnetCatalogRefreshSource,
error::{enforce_mainnet_network, subnet_cache_error},
subnet_catalog_path, subnet_catalog_refresh_lock_path,
};
use crate::{
cache_file::{
RefreshLockRequest, create_parent_directory, with_refresh_lock, write_text_atomically,
write_text_output,
},
ic_registry::MainnetRegistryFetchRequest,
subnet_catalog::{
SUBNET_CATALOG_REFRESH_REPORT_SCHEMA_VERSION, SubnetCatalogRefreshReport,
catalog_to_pretty_json, format_utc_timestamp_secs,
},
};
use std::path::PathBuf;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SubnetCatalogRefreshRequest {
pub cache: SubnetCatalogCacheRequest,
pub source_endpoint: String,
pub now_unix_secs: u64,
pub lock_stale_after_seconds: u64,
pub dry_run: bool,
pub output_path: Option<PathBuf>,
}
pub fn refresh_subnet_catalog(
request: &SubnetCatalogRefreshRequest,
) -> Result<SubnetCatalogRefreshReport, SubnetCatalogHostError> {
refresh_subnet_catalog_with_source(request, &LiveNnsRegistryRefreshSource)
}
pub fn refresh_subnet_catalog_with_source(
request: &SubnetCatalogRefreshRequest,
source: &dyn SubnetCatalogRefreshSource,
) -> Result<SubnetCatalogRefreshReport, SubnetCatalogHostError> {
enforce_mainnet_network(&request.cache.network)?;
let catalog_path = subnet_catalog_path(&request.cache.icp_root, &request.cache.network);
let lock_path =
subnet_catalog_refresh_lock_path(&request.cache.icp_root, &request.cache.network);
create_parent_directory(&catalog_path).map_err(subnet_cache_error)?;
with_refresh_lock(
RefreshLockRequest {
lock_path: &lock_path,
target_path: &catalog_path,
network: &request.cache.network,
now_unix_secs: request.now_unix_secs,
lock_stale_after_seconds: request.lock_stale_after_seconds,
},
subnet_cache_error,
|| {
let replaced_existing_catalog = catalog_path.is_file();
let fetched_at = format_utc_timestamp_secs(request.now_unix_secs);
let mut fetch_request = MainnetRegistryFetchRequest::new(fetched_at);
fetch_request.endpoint.clone_from(&request.source_endpoint);
let catalog = source.fetch_catalog(&fetch_request)?;
if catalog.network != request.cache.network {
return Err(SubnetCatalogHostError::RefreshNetworkMismatch {
requested: request.cache.network.clone(),
actual: catalog.network,
});
}
catalog.validate()?;
let catalog_json = catalog_to_pretty_json(&catalog)?;
if let Some(output_path) = &request.output_path {
write_text_output(output_path, &catalog_json).map_err(subnet_cache_error)?;
}
if !request.dry_run {
write_text_atomically(&catalog_path, &catalog_json).map_err(subnet_cache_error)?;
}
Ok(SubnetCatalogRefreshReport {
schema_version: SUBNET_CATALOG_REFRESH_REPORT_SCHEMA_VERSION,
network: catalog.network,
catalog_path: catalog_path.display().to_string(),
refresh_lock_path: lock_path.display().to_string(),
output_path: request
.output_path
.as_ref()
.map(|path| path.display().to_string()),
registry_canister_id: catalog.registry_canister_id,
registry_version: catalog.registry_version,
fetched_at: catalog.fetched_at,
source_endpoint: catalog.source_endpoint,
fetched_by: catalog.fetched_by,
dry_run: request.dry_run,
wrote_catalog: !request.dry_run,
replaced_existing_catalog,
subnet_count: catalog.subnets.len(),
routing_range_count: catalog.routing_ranges.len(),
})
},
)
}