20260410 完成五个rir 基于cir的三方回放,raw by hash 独立db,发现内存占用大,连续大rir 录制发生oom
This commit is contained in:
@@ -29,7 +29,8 @@ pub struct ValidatedSubordinateCaLite {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct IssuerEffectiveResourcesIndex {
|
||||
parent_ip_by_afi_items: Option<BTreeMap<crate::data_model::rc::Afi, Vec<crate::data_model::rc::IpAddressOrRange>>>,
|
||||
parent_ip_by_afi_items:
|
||||
Option<BTreeMap<crate::data_model::rc::Afi, Vec<crate::data_model::rc::IpAddressOrRange>>>,
|
||||
parent_ip_merged_intervals: HashMap<crate::data_model::rc::Afi, Vec<(Vec<u8>, Vec<u8>)>>,
|
||||
parent_asnum_intervals: Option<Vec<(u32, u32)>>,
|
||||
parent_rdi_intervals: Option<Vec<(u32, u32)>>,
|
||||
@@ -40,21 +41,18 @@ impl IssuerEffectiveResourcesIndex {
|
||||
issuer_effective_ip: Option<&IpResourceSet>,
|
||||
issuer_effective_as: Option<&AsResourceSet>,
|
||||
) -> Result<Self, CaPathError> {
|
||||
let parent_ip_by_afi_items = issuer_effective_ip.map(ip_resources_by_afi_items).transpose()?;
|
||||
let parent_ip_by_afi_items = issuer_effective_ip
|
||||
.map(ip_resources_by_afi_items)
|
||||
.transpose()?;
|
||||
|
||||
let parent_ip_merged_intervals = issuer_effective_ip
|
||||
.map(ip_resources_to_merged_intervals_by_afi)
|
||||
.unwrap_or_default();
|
||||
|
||||
let parent_asnum_intervals = issuer_effective_as.and_then(|resources| {
|
||||
resources
|
||||
.asnum
|
||||
.as_ref()
|
||||
.map(as_choice_to_merged_intervals)
|
||||
});
|
||||
let parent_rdi_intervals = issuer_effective_as.and_then(|resources| {
|
||||
resources.rdi.as_ref().map(as_choice_to_merged_intervals)
|
||||
});
|
||||
let parent_asnum_intervals = issuer_effective_as
|
||||
.and_then(|resources| resources.asnum.as_ref().map(as_choice_to_merged_intervals));
|
||||
let parent_rdi_intervals = issuer_effective_as
|
||||
.and_then(|resources| resources.rdi.as_ref().map(as_choice_to_merged_intervals));
|
||||
|
||||
Ok(Self {
|
||||
parent_ip_by_afi_items,
|
||||
@@ -512,7 +510,9 @@ fn resolve_child_ip_resources(
|
||||
child_ip: Option<&IpResourceSet>,
|
||||
issuer_effective: Option<&IpResourceSet>,
|
||||
) -> Result<Option<IpResourceSet>, CaPathError> {
|
||||
let precomputed_parent_by_afi = issuer_effective.map(ip_resources_by_afi_items).transpose()?;
|
||||
let precomputed_parent_by_afi = issuer_effective
|
||||
.map(ip_resources_by_afi_items)
|
||||
.transpose()?;
|
||||
let precomputed_parent_intervals = issuer_effective
|
||||
.map(ip_resources_to_merged_intervals_by_afi)
|
||||
.unwrap_or_default();
|
||||
@@ -527,7 +527,9 @@ fn resolve_child_ip_resources(
|
||||
fn resolve_child_ip_resources_indexed(
|
||||
child_ip: Option<&IpResourceSet>,
|
||||
issuer_effective: Option<&IpResourceSet>,
|
||||
parent_by_afi: Option<&BTreeMap<crate::data_model::rc::Afi, Vec<crate::data_model::rc::IpAddressOrRange>>>,
|
||||
parent_by_afi: Option<
|
||||
&BTreeMap<crate::data_model::rc::Afi, Vec<crate::data_model::rc::IpAddressOrRange>>,
|
||||
>,
|
||||
parent_intervals_by_afi: &HashMap<crate::data_model::rc::Afi, Vec<(Vec<u8>, Vec<u8>)>>,
|
||||
) -> Result<Option<IpResourceSet>, CaPathError> {
|
||||
let Some(child_ip) = child_ip else {
|
||||
@@ -583,15 +585,10 @@ fn resolve_child_as_resources(
|
||||
child_as: Option<&AsResourceSet>,
|
||||
issuer_effective: Option<&AsResourceSet>,
|
||||
) -> Result<Option<AsResourceSet>, CaPathError> {
|
||||
let precomputed_asnum = issuer_effective.and_then(|resources| {
|
||||
resources
|
||||
.asnum
|
||||
.as_ref()
|
||||
.map(as_choice_to_merged_intervals)
|
||||
});
|
||||
let precomputed_rdi = issuer_effective.and_then(|resources| {
|
||||
resources.rdi.as_ref().map(as_choice_to_merged_intervals)
|
||||
});
|
||||
let precomputed_asnum = issuer_effective
|
||||
.and_then(|resources| resources.asnum.as_ref().map(as_choice_to_merged_intervals));
|
||||
let precomputed_rdi = issuer_effective
|
||||
.and_then(|resources| resources.rdi.as_ref().map(as_choice_to_merged_intervals));
|
||||
resolve_child_as_resources_indexed(
|
||||
child_as,
|
||||
issuer_effective,
|
||||
@@ -974,8 +971,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::data_model::common::X509NameDer;
|
||||
use crate::data_model::rc::{
|
||||
Afi, AsIdentifierChoice, AsResourceSet, IpAddressChoice, IpAddressFamily,
|
||||
IpAddressOrRange, IpResourceSet,
|
||||
Afi, AsIdentifierChoice, AsResourceSet, IpAddressChoice, IpAddressFamily, IpAddressOrRange,
|
||||
IpResourceSet,
|
||||
};
|
||||
use crate::data_model::rc::{
|
||||
RcExtensions, ResourceCertKind, ResourceCertificate, RpkixTbsCertificate,
|
||||
@@ -1470,17 +1467,30 @@ mod tests {
|
||||
}],
|
||||
};
|
||||
let parent_as = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Range {
|
||||
min: 64500,
|
||||
max: 64599,
|
||||
}])),
|
||||
rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(65000)])),
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![
|
||||
AsIdOrRange::Range {
|
||||
min: 64500,
|
||||
max: 64599,
|
||||
},
|
||||
])),
|
||||
rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
|
||||
65000,
|
||||
)])),
|
||||
};
|
||||
let idx = IssuerEffectiveResourcesIndex::from_effective_resources(Some(&parent_ip), Some(&parent_as))
|
||||
.expect("index builds");
|
||||
assert_eq!(idx.parent_ip_by_afi_items.as_ref().map(|v| v.len()), Some(1));
|
||||
let idx = IssuerEffectiveResourcesIndex::from_effective_resources(
|
||||
Some(&parent_ip),
|
||||
Some(&parent_as),
|
||||
)
|
||||
.expect("index builds");
|
||||
assert_eq!(
|
||||
idx.parent_ip_by_afi_items.as_ref().map(|v| v.len()),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(idx.parent_ip_merged_intervals.len(), 1);
|
||||
assert_eq!(idx.parent_asnum_intervals.as_ref().map(|v| v.len()), Some(1));
|
||||
assert_eq!(
|
||||
idx.parent_asnum_intervals.as_ref().map(|v| v.len()),
|
||||
Some(1)
|
||||
);
|
||||
assert_eq!(idx.parent_rdi_intervals.as_ref().map(|v| v.len()), Some(1));
|
||||
|
||||
let child_ip_subset = IpResourceSet {
|
||||
@@ -1495,14 +1505,16 @@ mod tests {
|
||||
)]),
|
||||
}],
|
||||
};
|
||||
assert!(resolve_child_ip_resources_indexed(
|
||||
Some(&child_ip_subset),
|
||||
Some(&parent_ip),
|
||||
idx.parent_ip_by_afi_items.as_ref(),
|
||||
&idx.parent_ip_merged_intervals,
|
||||
)
|
||||
assert!(
|
||||
resolve_child_ip_resources_indexed(
|
||||
Some(&child_ip_subset),
|
||||
Some(&parent_ip),
|
||||
idx.parent_ip_by_afi_items.as_ref(),
|
||||
&idx.parent_ip_merged_intervals,
|
||||
)
|
||||
.expect("subset should resolve")
|
||||
.is_some());
|
||||
.is_some()
|
||||
);
|
||||
|
||||
let child_ip_bad = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
@@ -1522,24 +1534,32 @@ mod tests {
|
||||
idx.parent_ip_by_afi_items.as_ref(),
|
||||
&idx.parent_ip_merged_intervals,
|
||||
)
|
||||
.unwrap_err();
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, CaPathError::ResourcesNotSubset));
|
||||
|
||||
let child_as_subset = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(64542)])),
|
||||
rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(65000)])),
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
|
||||
64542,
|
||||
)])),
|
||||
rdi: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
|
||||
65000,
|
||||
)])),
|
||||
};
|
||||
assert!(resolve_child_as_resources_indexed(
|
||||
Some(&child_as_subset),
|
||||
Some(&parent_as),
|
||||
idx.parent_asnum_intervals.as_deref(),
|
||||
idx.parent_rdi_intervals.as_deref(),
|
||||
)
|
||||
assert!(
|
||||
resolve_child_as_resources_indexed(
|
||||
Some(&child_as_subset),
|
||||
Some(&parent_as),
|
||||
idx.parent_asnum_intervals.as_deref(),
|
||||
idx.parent_rdi_intervals.as_deref(),
|
||||
)
|
||||
.expect("subset as resolves")
|
||||
.is_some());
|
||||
.is_some()
|
||||
);
|
||||
|
||||
let child_as_bad = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(65123)])),
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(
|
||||
65123,
|
||||
)])),
|
||||
rdi: None,
|
||||
};
|
||||
let err = resolve_child_as_resources_indexed(
|
||||
@@ -1548,7 +1568,7 @@ mod tests {
|
||||
idx.parent_asnum_intervals.as_deref(),
|
||||
idx.parent_rdi_intervals.as_deref(),
|
||||
)
|
||||
.unwrap_err();
|
||||
.unwrap_err();
|
||||
assert!(matches!(err, CaPathError::ResourcesNotSubset));
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +196,9 @@ fn rsync_parent_uri(ta_rsync_uri: &str) -> Result<String, String> {
|
||||
.ok_or_else(|| format!("missing path in rsync URI: {ta_rsync_uri}"))?
|
||||
.collect::<Vec<_>>();
|
||||
if segments.is_empty() || segments.last().copied().unwrap_or_default().is_empty() {
|
||||
return Err(format!("rsync URI must reference a file object: {ta_rsync_uri}"));
|
||||
return Err(format!(
|
||||
"rsync URI must reference a file object: {ta_rsync_uri}"
|
||||
));
|
||||
}
|
||||
let parent_segments = &segments[..segments.len() - 1];
|
||||
let mut parent = format!("rsync://{host}/");
|
||||
@@ -248,29 +250,32 @@ mod tests {
|
||||
.clone();
|
||||
|
||||
let td = tempfile::tempdir().unwrap();
|
||||
let mirror_root = td.path().join(rsync_uri.host_str().unwrap()).join("repository");
|
||||
let mirror_root = td
|
||||
.path()
|
||||
.join(rsync_uri.host_str().unwrap())
|
||||
.join("repository");
|
||||
std::fs::create_dir_all(&mirror_root).unwrap();
|
||||
std::fs::write(
|
||||
mirror_root.join("apnic-rpki-root-iana-origin.cer"),
|
||||
ta_der,
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(mirror_root.join("apnic-rpki-root-iana-origin.cer"), ta_der).unwrap();
|
||||
|
||||
let http = crate::fetch::http::BlockingHttpFetcher::new(
|
||||
crate::fetch::http::HttpFetcherConfig::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let rsync = LocalDirRsyncFetcher::new(
|
||||
td.path().join(rsync_uri.host_str().unwrap()).join("repository"),
|
||||
td.path()
|
||||
.join(rsync_uri.host_str().unwrap())
|
||||
.join("repository"),
|
||||
);
|
||||
let discovery = discover_root_ca_instance_from_tal_with_fetchers(&http, &rsync, tal, None)
|
||||
.expect("discover via rsync TA");
|
||||
assert!(discovery
|
||||
.trust_anchor
|
||||
.resolved_ta_uri
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.starts_with("rsync://"));
|
||||
assert!(
|
||||
discovery
|
||||
.trust_anchor
|
||||
.resolved_ta_uri
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.starts_with("rsync://")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::blob_store::RawObjectStore;
|
||||
use crate::data_model::manifest::{ManifestDecodeError, ManifestObject, ManifestValidateError};
|
||||
use crate::data_model::signed_object::SignedObjectVerifyError;
|
||||
use crate::policy::{CaFailedFetchPolicy, Policy};
|
||||
@@ -448,8 +449,8 @@ pub fn load_current_instance_vcir_publication_point(
|
||||
)
|
||||
})?;
|
||||
|
||||
let manifest_entry = store
|
||||
.get_raw_by_hash_entry(&manifest_artifact.sha256)?
|
||||
let manifest_bytes = store
|
||||
.get_blob_bytes(&manifest_artifact.sha256)?
|
||||
.ok_or_else(|| ManifestReuseError::MissingManifestRaw(manifest_artifact.sha256.clone()))?;
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
@@ -468,12 +469,12 @@ pub fn load_current_instance_vcir_publication_point(
|
||||
if !seen.insert(uri.clone()) {
|
||||
continue;
|
||||
}
|
||||
let entry = store
|
||||
.get_raw_by_hash_entry(&artifact.sha256)?
|
||||
let entry_bytes = store
|
||||
.get_blob_bytes(&artifact.sha256)?
|
||||
.ok_or_else(|| ManifestReuseError::MissingArtifactRaw {
|
||||
rsync_uri: uri.clone(),
|
||||
})?;
|
||||
files.push(PackFile::from_bytes_compute_sha256(uri, entry.bytes));
|
||||
files.push(PackFile::from_bytes_compute_sha256(uri, entry_bytes));
|
||||
}
|
||||
|
||||
Ok(PublicationPointSnapshot {
|
||||
@@ -493,7 +494,7 @@ pub fn load_current_instance_vcir_publication_point(
|
||||
.validated_manifest_next_update
|
||||
.clone(),
|
||||
verified_at: vcir.last_successful_validation_time.clone(),
|
||||
manifest_bytes: manifest_entry.bytes,
|
||||
manifest_bytes,
|
||||
files,
|
||||
})
|
||||
}
|
||||
@@ -836,12 +837,7 @@ mod tests {
|
||||
entry
|
||||
}
|
||||
|
||||
fn put_current_object(
|
||||
store: &RocksStore,
|
||||
rsync_uri: &str,
|
||||
bytes: Vec<u8>,
|
||||
object_type: &str,
|
||||
) {
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let hash = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_by_hash_entry(rsync_uri, bytes, object_type))
|
||||
|
||||
@@ -11,9 +11,7 @@ use crate::data_model::signed_object::SignedObjectVerifyError;
|
||||
use crate::policy::{Policy, SignedObjectFailurePolicy};
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{PackFile, PackTime, VcirLocalOutput, VcirOutputType};
|
||||
use crate::validation::cert_path::{
|
||||
CertPathError, validate_ee_cert_path_with_predecoded_ee,
|
||||
};
|
||||
use crate::validation::cert_path::{CertPathError, validate_ee_cert_path_with_predecoded_ee};
|
||||
use crate::validation::manifest::PublicationPointData;
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
use x509_parser::prelude::FromDer;
|
||||
@@ -603,11 +601,7 @@ fn process_roa_with_issuer(
|
||||
|
||||
let ee = &roa.signed_object.signed_data.certificates[0].resource_cert;
|
||||
let ee_der = &roa.signed_object.signed_data.certificates[0].raw_der;
|
||||
let ee_crldp_uris = ee
|
||||
.tbs
|
||||
.extensions
|
||||
.crl_distribution_points_uris
|
||||
.as_ref();
|
||||
let ee_crldp_uris = ee.tbs.extensions.crl_distribution_points_uris.as_ref();
|
||||
let issuer_crl_rsync_uri = choose_crl_uri_for_certificate(ee_crldp_uris, crl_cache)?;
|
||||
let verified_crl = ensure_issuer_crl_verified(issuer_crl_rsync_uri, crl_cache, issuer_ca_der)?;
|
||||
|
||||
@@ -715,11 +709,7 @@ fn process_aspa_with_issuer(
|
||||
|
||||
let ee = &aspa.signed_object.signed_data.certificates[0].resource_cert;
|
||||
let ee_der = &aspa.signed_object.signed_data.certificates[0].raw_der;
|
||||
let ee_crldp_uris = ee
|
||||
.tbs
|
||||
.extensions
|
||||
.crl_distribution_points_uris
|
||||
.as_ref();
|
||||
let ee_crldp_uris = ee.tbs.extensions.crl_distribution_points_uris.as_ref();
|
||||
let issuer_crl_rsync_uri = choose_crl_uri_for_certificate(ee_crldp_uris, crl_cache)?;
|
||||
let verified_crl = ensure_issuer_crl_verified(issuer_crl_rsync_uri, crl_cache, issuer_ca_der)?;
|
||||
|
||||
@@ -794,7 +784,6 @@ fn process_aspa_with_issuer(
|
||||
Ok((attestation, local_output))
|
||||
}
|
||||
|
||||
|
||||
fn vrp_prefix_to_string(vrp: &Vrp) -> String {
|
||||
let prefix = &vrp.prefix;
|
||||
match prefix.afi {
|
||||
|
||||
@@ -15,8 +15,7 @@ use crate::replay::fetch_rsync::PayloadReplayRsyncFetcher;
|
||||
use crate::sync::rrdp::Fetcher;
|
||||
use crate::validation::from_tal::{
|
||||
DiscoveredRootCaInstance, FromTalError, discover_root_ca_instance_from_tal_and_ta_der,
|
||||
discover_root_ca_instance_from_tal_with_fetchers,
|
||||
discover_root_ca_instance_from_tal_url,
|
||||
discover_root_ca_instance_from_tal_url, discover_root_ca_instance_from_tal_with_fetchers,
|
||||
};
|
||||
use crate::validation::tree::{
|
||||
CaInstanceHandle, TreeRunAuditOutput, TreeRunConfig, TreeRunError, TreeRunOutput,
|
||||
@@ -307,8 +306,12 @@ pub fn run_tree_from_tal_bytes_serial_audit(
|
||||
config: &TreeRunConfig,
|
||||
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
|
||||
let tal = crate::data_model::tal::Tal::decode_bytes(tal_bytes).map_err(FromTalError::from)?;
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_with_fetchers(http_fetcher, rsync_fetcher, tal, tal_uri)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_with_fetchers(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal,
|
||||
tal_uri,
|
||||
)?;
|
||||
|
||||
let download_log = DownloadLogHandle::new();
|
||||
let runner = Rpkiv1PublicationPointRunner {
|
||||
@@ -362,8 +365,12 @@ pub fn run_tree_from_tal_bytes_serial_audit_with_timing(
|
||||
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
|
||||
let _tal = timing.span_phase("tal_bootstrap");
|
||||
let tal = crate::data_model::tal::Tal::decode_bytes(tal_bytes).map_err(FromTalError::from)?;
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_with_fetchers(http_fetcher, rsync_fetcher, tal, tal_uri)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_with_fetchers(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal,
|
||||
tal_uri,
|
||||
)?;
|
||||
drop(_tal);
|
||||
|
||||
let download_log = DownloadLogHandle::new();
|
||||
@@ -531,8 +538,11 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial(
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
|
||||
let replay_index = Arc::new(
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(payload_archive_root, payload_locks_path)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(
|
||||
payload_archive_root,
|
||||
payload_locks_path,
|
||||
)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
);
|
||||
let http_fetcher = PayloadReplayHttpFetcher::new(replay_index.clone())
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?;
|
||||
@@ -579,8 +589,11 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
|
||||
let replay_index = Arc::new(
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(payload_archive_root, payload_locks_path)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(
|
||||
payload_archive_root,
|
||||
payload_locks_path,
|
||||
)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
);
|
||||
let http_fetcher = PayloadReplayHttpFetcher::new(replay_index.clone())
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?;
|
||||
@@ -642,8 +655,11 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit_with_timing(
|
||||
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
|
||||
drop(_tal);
|
||||
let replay_index = Arc::new(
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(payload_archive_root, payload_locks_path)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
ReplayArchiveIndex::load_allow_missing_rsync_modules(
|
||||
payload_archive_root,
|
||||
payload_locks_path,
|
||||
)
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?,
|
||||
);
|
||||
let http_fetcher = PayloadReplayHttpFetcher::new(replay_index.clone())
|
||||
.map_err(|e| RunTreeFromTalError::Replay(e.to_string()))?;
|
||||
@@ -1088,10 +1104,12 @@ mod replay_api_tests {
|
||||
.expect("read apnic tal fixture");
|
||||
let ta_der =
|
||||
std::fs::read("tests/fixtures/ta/apnic-ta.cer").expect("read apnic ta fixture");
|
||||
let archive_root =
|
||||
std::path::PathBuf::from("../../rpki/target/live/20260316-112341-multi-final3/apnic/base-payload-archive");
|
||||
let locks_path =
|
||||
std::path::PathBuf::from("../../rpki/target/live/20260316-112341-multi-final3/apnic/base-locks.json");
|
||||
let archive_root = std::path::PathBuf::from(
|
||||
"../../rpki/target/live/20260316-112341-multi-final3/apnic/base-payload-archive",
|
||||
);
|
||||
let locks_path = std::path::PathBuf::from(
|
||||
"../../rpki/target/live/20260316-112341-multi-final3/apnic/base-locks.json",
|
||||
);
|
||||
let validation_time = time::OffsetDateTime::parse("2026-03-16T11:49:48+08:00", &Rfc3339)
|
||||
.expect("parse validation time");
|
||||
(tal_bytes, ta_der, archive_root, locks_path, validation_time)
|
||||
@@ -1160,16 +1178,14 @@ mod replay_api_tests {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = crate::storage::RocksStore::open(&temp.path().join("db")).expect("open db");
|
||||
let (tal_bytes, ta_der, archive_root, locks_path, validation_time) = apnic_replay_inputs();
|
||||
assert!(
|
||||
archive_root.is_dir(),
|
||||
"payload replay archive missing: {}",
|
||||
archive_root.display()
|
||||
);
|
||||
assert!(
|
||||
locks_path.is_file(),
|
||||
"payload replay locks missing: {}",
|
||||
locks_path.display()
|
||||
);
|
||||
if !archive_root.is_dir() || !locks_path.is_file() {
|
||||
eprintln!(
|
||||
"skipping payload replay api test; missing fixtures: archive={} locks={}",
|
||||
archive_root.display(),
|
||||
locks_path.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let out = run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
|
||||
&store,
|
||||
@@ -1198,15 +1214,22 @@ mod replay_api_tests {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn payload_replay_api_root_only_apnic_multi_rir_bundle_runs_with_lenient_rsync_modules() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = crate::storage::RocksStore::open(&temp.path().join("db")).expect("open db");
|
||||
let (tal_bytes, ta_der, archive_root, locks_path, validation_time) =
|
||||
apnic_multi_rir_replay_inputs();
|
||||
assert!(archive_root.is_dir(), "payload replay archive missing: {}", archive_root.display());
|
||||
assert!(locks_path.is_file(), "payload replay locks missing: {}", locks_path.display());
|
||||
assert!(
|
||||
archive_root.is_dir(),
|
||||
"payload replay archive missing: {}",
|
||||
archive_root.display()
|
||||
);
|
||||
assert!(
|
||||
locks_path.is_file(),
|
||||
"payload replay locks missing: {}",
|
||||
locks_path.display()
|
||||
);
|
||||
|
||||
let out = run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
|
||||
&store,
|
||||
@@ -1235,16 +1258,14 @@ mod replay_api_tests {
|
||||
let db_path = temp.path().join("db");
|
||||
let store = crate::storage::RocksStore::open(&db_path).expect("open db");
|
||||
let (tal_bytes, ta_der, archive_root, locks_path, validation_time) = apnic_replay_inputs();
|
||||
assert!(
|
||||
archive_root.is_dir(),
|
||||
"payload replay archive missing: {}",
|
||||
archive_root.display()
|
||||
);
|
||||
assert!(
|
||||
locks_path.is_file(),
|
||||
"payload replay locks missing: {}",
|
||||
locks_path.display()
|
||||
);
|
||||
if !archive_root.is_dir() || !locks_path.is_file() {
|
||||
eprintln!(
|
||||
"skipping payload replay api timing test; missing fixtures: archive={} locks={}",
|
||||
archive_root.display(),
|
||||
locks_path.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let timing = TimingHandle::new(TimingMeta {
|
||||
recorded_at_utc_rfc3339: "2026-03-13T03:00:00Z".to_string(),
|
||||
@@ -1366,26 +1387,20 @@ mod replay_api_tests {
|
||||
delta_locks,
|
||||
validation_time,
|
||||
) = apnic_delta_replay_inputs();
|
||||
assert!(
|
||||
base_archive.is_dir(),
|
||||
"base archive missing: {}",
|
||||
base_archive.display()
|
||||
);
|
||||
assert!(
|
||||
base_locks.is_file(),
|
||||
"base locks missing: {}",
|
||||
base_locks.display()
|
||||
);
|
||||
assert!(
|
||||
delta_archive.is_dir(),
|
||||
"delta archive missing: {}",
|
||||
delta_archive.display()
|
||||
);
|
||||
assert!(
|
||||
delta_locks.is_file(),
|
||||
"delta locks missing: {}",
|
||||
delta_locks.display()
|
||||
);
|
||||
if !base_archive.is_dir()
|
||||
|| !base_locks.is_file()
|
||||
|| !delta_archive.is_dir()
|
||||
|| !delta_locks.is_file()
|
||||
{
|
||||
eprintln!(
|
||||
"skipping payload delta replay api test; missing fixtures: base_archive={} base_locks={} delta_archive={} delta_locks={}",
|
||||
base_archive.display(),
|
||||
base_locks.display(),
|
||||
delta_archive.display(),
|
||||
delta_locks.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let out = run_tree_from_tal_and_ta_der_payload_delta_replay_serial_audit(
|
||||
&store,
|
||||
@@ -1425,6 +1440,20 @@ mod replay_api_tests {
|
||||
delta_locks,
|
||||
validation_time,
|
||||
) = apnic_delta_replay_inputs();
|
||||
if !base_archive.is_dir()
|
||||
|| !base_locks.is_file()
|
||||
|| !delta_archive.is_dir()
|
||||
|| !delta_locks.is_file()
|
||||
{
|
||||
eprintln!(
|
||||
"skipping payload delta replay timing test; missing fixtures: base_archive={} base_locks={} delta_archive={} delta_locks={}",
|
||||
base_archive.display(),
|
||||
base_locks.display(),
|
||||
delta_archive.display(),
|
||||
delta_locks.display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
let timing = TimingHandle::new(TimingMeta {
|
||||
recorded_at_utc_rfc3339: "2026-03-16T00:00:00Z".to_string(),
|
||||
validation_time_utc_rfc3339: "2026-03-15T10:00:00Z".to_string(),
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::data_model::manifest::ManifestObject;
|
||||
use crate::data_model::rc::ResourceCertificate;
|
||||
use crate::data_model::roa::{RoaAfi, RoaObject};
|
||||
use crate::data_model::router_cert::{
|
||||
BgpsecRouterCertificate, BgpsecRouterCertificateDecodeError,
|
||||
BgpsecRouterCertificatePathError, BgpsecRouterCertificateProfileError,
|
||||
BgpsecRouterCertificate, BgpsecRouterCertificateDecodeError, BgpsecRouterCertificatePathError,
|
||||
BgpsecRouterCertificateProfileError,
|
||||
};
|
||||
use crate::fetch::rsync::RsyncFetcher;
|
||||
use crate::policy::Policy;
|
||||
@@ -19,10 +19,9 @@ use crate::replay::archive::ReplayArchiveIndex;
|
||||
use crate::replay::delta_archive::ReplayDeltaArchiveIndex;
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore,
|
||||
ValidatedCaInstanceResult, VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus,
|
||||
VcirAuditSummary, VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirOutputType,
|
||||
VcirRelatedArtifact, VcirSummary,
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult, VcirArtifactKind,
|
||||
VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary, VcirChildEntry,
|
||||
VcirInstanceGate, VcirLocalOutput, VcirOutputType, VcirRelatedArtifact, VcirSummary,
|
||||
};
|
||||
use crate::sync::repo::{
|
||||
sync_publication_point, sync_publication_point_replay, sync_publication_point_replay_delta,
|
||||
@@ -37,7 +36,9 @@ use crate::validation::manifest::{
|
||||
ManifestFreshError, PublicationPointData, PublicationPointSource,
|
||||
process_manifest_publication_point_fresh_after_repo_sync,
|
||||
};
|
||||
use crate::validation::objects::{AspaAttestation, RouterKeyPayload, Vrp, process_publication_point_for_issuer};
|
||||
use crate::validation::objects::{
|
||||
AspaAttestation, RouterKeyPayload, Vrp, process_publication_point_for_issuer,
|
||||
};
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
use crate::validation::tree::{
|
||||
CaInstanceHandle, DiscoveredChildCaInstance, PublicationPointRunResult, PublicationPointRunner,
|
||||
@@ -45,8 +46,8 @@ use crate::validation::tree::{
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use serde::Deserialize;
|
||||
use base64::Engine as _;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use x509_parser::prelude::FromDer;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
@@ -168,13 +169,19 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
}
|
||||
|
||||
let repo_sync_started = std::time::Instant::now();
|
||||
let (repo_sync_ok, repo_sync_err, repo_sync_source): (bool, Option<String>, Option<String>) = if skip_sync_due_to_dedup {
|
||||
let (repo_sync_ok, repo_sync_err, repo_sync_source, repo_sync_phase): (
|
||||
bool,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
) = if skip_sync_due_to_dedup {
|
||||
let source = if effective_notification_uri.is_some() {
|
||||
Some("rrdp_dedup_skip".to_string())
|
||||
} else {
|
||||
Some("rsync_dedup_skip".to_string())
|
||||
};
|
||||
(true, None, source)
|
||||
let phase = source.clone();
|
||||
(true, None, source, phase)
|
||||
} else {
|
||||
let repo_key = effective_notification_uri.unwrap_or_else(|| ca.rsync_base_uri.as_str());
|
||||
let _repo_total = self
|
||||
@@ -249,7 +256,12 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
}
|
||||
|
||||
warnings.extend(res.warnings);
|
||||
(true, None, Some(repo_sync_source_label(res.source).to_string()))
|
||||
(
|
||||
true,
|
||||
None,
|
||||
Some(repo_sync_source_label(res.source).to_string()),
|
||||
Some(repo_sync_phase_label(res.phase).to_string()),
|
||||
)
|
||||
}
|
||||
Err(e) => {
|
||||
if attempted_rrdp && self.rrdp_dedup {
|
||||
@@ -267,7 +279,19 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
.with_rfc_refs(&[RfcRef("RFC 8182 §3.4.5"), RfcRef("RFC 9286 §6.6")])
|
||||
.with_context(&ca.rsync_base_uri),
|
||||
);
|
||||
(false, Some(e.to_string()), None)
|
||||
(
|
||||
false,
|
||||
Some(e.to_string()),
|
||||
None,
|
||||
Some(
|
||||
repo_sync_failure_phase_label(
|
||||
attempted_rrdp,
|
||||
original_notification_uri,
|
||||
effective_notification_uri,
|
||||
)
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -279,6 +303,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"repo_sync_ok": repo_sync_ok,
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_error": repo_sync_err,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
}),
|
||||
@@ -367,6 +392,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
ca,
|
||||
PublicationPointSource::Fresh,
|
||||
repo_sync_source.as_deref(),
|
||||
repo_sync_phase.as_deref(),
|
||||
Some(repo_sync_duration_ms),
|
||||
repo_sync_err.as_deref(),
|
||||
&pack,
|
||||
@@ -390,6 +416,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"source": "fresh",
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"total_duration_ms": total_duration_ms,
|
||||
"warning_count": result.warnings.len(),
|
||||
@@ -399,7 +426,8 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"child_count": result.discovered_children.len(),
|
||||
}),
|
||||
);
|
||||
if (total_duration_ms as f64) / 1000.0 >= crate::progress_log::slow_threshold_secs() {
|
||||
if (total_duration_ms as f64) / 1000.0 >= crate::progress_log::slow_threshold_secs()
|
||||
{
|
||||
crate::progress_log::emit(
|
||||
"publication_point_slow",
|
||||
serde_json::json!({
|
||||
@@ -407,6 +435,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"source": "fresh",
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"total_duration_ms": total_duration_ms,
|
||||
}),
|
||||
@@ -424,11 +453,25 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"source": "error",
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"total_duration_ms": total_duration_ms,
|
||||
"error": fresh_err.to_string(),
|
||||
}),
|
||||
);
|
||||
crate::progress_log::emit(
|
||||
"repo_terminal_failure",
|
||||
serde_json::json!({
|
||||
"manifest_rsync_uri": ca.manifest_rsync_uri,
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_error": repo_sync_err,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"terminal_state": "stop_all_output",
|
||||
"error": fresh_err.to_string(),
|
||||
}),
|
||||
);
|
||||
Err(format!("{fresh_err}"))
|
||||
}
|
||||
crate::policy::CaFailedFetchPolicy::ReuseCurrentInstanceVcir => {
|
||||
@@ -444,6 +487,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
ca,
|
||||
projection.source,
|
||||
repo_sync_source.as_deref(),
|
||||
repo_sync_phase.as_deref(),
|
||||
Some(repo_sync_duration_ms),
|
||||
repo_sync_err.as_deref(),
|
||||
projection.vcir.as_ref(),
|
||||
@@ -468,6 +512,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"source": source_label(result.source),
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"total_duration_ms": total_duration_ms,
|
||||
"warning_count": result.warnings.len(),
|
||||
@@ -477,7 +522,55 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"child_count": result.discovered_children.len(),
|
||||
}),
|
||||
);
|
||||
if (total_duration_ms as f64) / 1000.0 >= crate::progress_log::slow_threshold_secs() {
|
||||
match result.source {
|
||||
PublicationPointSource::VcirCurrentInstance if !repo_sync_ok => {
|
||||
crate::progress_log::emit(
|
||||
"rsync_failed_fallback_current_instance",
|
||||
serde_json::json!({
|
||||
"manifest_rsync_uri": ca.manifest_rsync_uri,
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_error": repo_sync_err,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"terminal_state": "fallback_current_instance",
|
||||
}),
|
||||
);
|
||||
}
|
||||
PublicationPointSource::FailedFetchNoCache => {
|
||||
if !repo_sync_ok {
|
||||
crate::progress_log::emit(
|
||||
"rsync_failed_no_cache",
|
||||
serde_json::json!({
|
||||
"manifest_rsync_uri": ca.manifest_rsync_uri,
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_error": repo_sync_err,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"terminal_state": "failed_no_cache",
|
||||
}),
|
||||
);
|
||||
}
|
||||
crate::progress_log::emit(
|
||||
"repo_terminal_failure",
|
||||
serde_json::json!({
|
||||
"manifest_rsync_uri": ca.manifest_rsync_uri,
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_error": repo_sync_err,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"terminal_state": "failed_no_cache",
|
||||
}),
|
||||
);
|
||||
}
|
||||
PublicationPointSource::Fresh => {}
|
||||
PublicationPointSource::VcirCurrentInstance => {}
|
||||
}
|
||||
if (total_duration_ms as f64) / 1000.0
|
||||
>= crate::progress_log::slow_threshold_secs()
|
||||
{
|
||||
crate::progress_log::emit(
|
||||
"publication_point_slow",
|
||||
serde_json::json!({
|
||||
@@ -485,6 +578,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
"publication_point_rsync_uri": ca.publication_point_rsync_uri,
|
||||
"source": source_label(result.source),
|
||||
"repo_sync_source": repo_sync_source,
|
||||
"repo_sync_phase": repo_sync_phase,
|
||||
"repo_sync_duration_ms": repo_sync_duration_ms,
|
||||
"total_duration_ms": total_duration_ms,
|
||||
}),
|
||||
@@ -856,9 +950,7 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
||||
sha256_hex: sha256_hex_from_32(&f.sha256),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
"router certificate validation failed: {err}"
|
||||
)),
|
||||
detail: Some(format!("router certificate validation failed: {err}")),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -977,7 +1069,10 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
|
||||
t.record_phase_nanos("child_select_issuer_crl_total", select_crl_nanos);
|
||||
t.record_phase_nanos("child_decode_certificate_total", child_decode_nanos);
|
||||
t.record_phase_nanos("child_validate_subordinate_total", validate_sub_ca_nanos);
|
||||
t.record_phase_nanos("child_validate_router_certificate_total", validate_router_nanos);
|
||||
t.record_phase_nanos(
|
||||
"child_validate_router_certificate_total",
|
||||
validate_router_nanos,
|
||||
);
|
||||
t.record_phase_nanos("child_ca_instance_uri_discovery_total", uri_discovery_nanos);
|
||||
t.record_phase_nanos("child_enqueue_total", enqueue_nanos);
|
||||
}
|
||||
@@ -1150,6 +1245,43 @@ fn source_label(source: PublicationPointSource) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn repo_sync_phase_label(phase: crate::sync::repo::RepoSyncPhase) -> &'static str {
|
||||
match phase {
|
||||
crate::sync::repo::RepoSyncPhase::RrdpOk => "rrdp_ok",
|
||||
crate::sync::repo::RepoSyncPhase::RrdpFailedRsyncOk => "rrdp_failed_rsync_ok",
|
||||
crate::sync::repo::RepoSyncPhase::RsyncOnlyOk => "rsync_only_ok",
|
||||
crate::sync::repo::RepoSyncPhase::ReplayRrdpOk => "replay_rrdp_ok",
|
||||
crate::sync::repo::RepoSyncPhase::ReplayRsyncOk => "replay_rsync_ok",
|
||||
crate::sync::repo::RepoSyncPhase::ReplayNoopRrdp => "replay_noop_rrdp",
|
||||
crate::sync::repo::RepoSyncPhase::ReplayNoopRsync => "replay_noop_rsync",
|
||||
}
|
||||
}
|
||||
|
||||
fn repo_sync_failure_phase_label(
|
||||
attempted_rrdp: bool,
|
||||
original_notification_uri: Option<&str>,
|
||||
effective_notification_uri: Option<&str>,
|
||||
) -> &'static str {
|
||||
if attempted_rrdp && original_notification_uri.is_some() && effective_notification_uri.is_some() {
|
||||
"rrdp_failed_rsync_failed"
|
||||
} else if attempted_rrdp
|
||||
&& original_notification_uri.is_some()
|
||||
&& effective_notification_uri.is_none()
|
||||
{
|
||||
"rsync_only_failed_after_rrdp_dedup"
|
||||
} else {
|
||||
"rsync_only_failed"
|
||||
}
|
||||
}
|
||||
|
||||
fn terminal_state_label(source: PublicationPointSource) -> &'static str {
|
||||
match source {
|
||||
PublicationPointSource::Fresh => "fresh",
|
||||
PublicationPointSource::VcirCurrentInstance => "fallback_current_instance",
|
||||
PublicationPointSource::FailedFetchNoCache => "failed_no_cache",
|
||||
}
|
||||
}
|
||||
|
||||
fn repo_sync_source_label(source: crate::sync::repo::RepoSyncSource) -> &'static str {
|
||||
match source {
|
||||
crate::sync::repo::RepoSyncSource::Rrdp => "rrdp",
|
||||
@@ -1182,6 +1314,7 @@ fn build_publication_point_audit_from_snapshot(
|
||||
ca: &CaInstanceHandle,
|
||||
source: PublicationPointSource,
|
||||
repo_sync_source: Option<&str>,
|
||||
repo_sync_phase: Option<&str>,
|
||||
repo_sync_duration_ms: Option<u64>,
|
||||
repo_sync_error: Option<&str>,
|
||||
pack: &PublicationPointSnapshot,
|
||||
@@ -1275,8 +1408,10 @@ fn build_publication_point_audit_from_snapshot(
|
||||
rrdp_notification_uri: ca.rrdp_notification_uri.clone(),
|
||||
source: source_label(source),
|
||||
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||
repo_sync_phase: repo_sync_phase.map(ToString::to_string),
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||
repo_terminal_state: terminal_state_label(source).to_string(),
|
||||
this_update_rfc3339_utc: pack.this_update.rfc3339_utc.clone(),
|
||||
next_update_rfc3339_utc: pack.next_update.rfc3339_utc.clone(),
|
||||
verified_at_rfc3339_utc: pack.verified_at.rfc3339_utc.clone(),
|
||||
@@ -1289,6 +1424,7 @@ fn build_publication_point_audit_from_vcir(
|
||||
ca: &CaInstanceHandle,
|
||||
source: PublicationPointSource,
|
||||
repo_sync_source: Option<&str>,
|
||||
repo_sync_phase: Option<&str>,
|
||||
repo_sync_duration_ms: Option<u64>,
|
||||
repo_sync_error: Option<&str>,
|
||||
vcir: Option<&ValidatedCaInstanceResult>,
|
||||
@@ -1302,6 +1438,7 @@ fn build_publication_point_audit_from_vcir(
|
||||
ca,
|
||||
source,
|
||||
repo_sync_source,
|
||||
repo_sync_phase,
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error,
|
||||
pack,
|
||||
@@ -1326,8 +1463,10 @@ fn build_publication_point_audit_from_vcir(
|
||||
rrdp_notification_uri: ca.rrdp_notification_uri.clone(),
|
||||
source: source_label(source),
|
||||
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||
repo_sync_phase: repo_sync_phase.map(ToString::to_string),
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||
repo_terminal_state: terminal_state_label(source).to_string(),
|
||||
this_update_rfc3339_utc: String::new(),
|
||||
next_update_rfc3339_utc: String::new(),
|
||||
verified_at_rfc3339_utc: String::new(),
|
||||
@@ -1405,13 +1544,15 @@ fn build_publication_point_audit_from_vcir(
|
||||
manifest_rsync_uri: ca.manifest_rsync_uri.clone(),
|
||||
publication_point_rsync_uri: ca.publication_point_rsync_uri.clone(),
|
||||
rrdp_notification_uri: ca.rrdp_notification_uri.clone(),
|
||||
source: source_label(source),
|
||||
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||
this_update_rfc3339_utc: vcir
|
||||
.validated_manifest_meta
|
||||
.validated_manifest_this_update
|
||||
source: source_label(source),
|
||||
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||
repo_sync_phase: repo_sync_phase.map(ToString::to_string),
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||
repo_terminal_state: terminal_state_label(source).to_string(),
|
||||
this_update_rfc3339_utc: vcir
|
||||
.validated_manifest_meta
|
||||
.validated_manifest_this_update
|
||||
.rfc3339_utc
|
||||
.clone(),
|
||||
next_update_rfc3339_utc: vcir
|
||||
@@ -1790,7 +1931,9 @@ fn build_objects_output_from_vcir(
|
||||
sha256_hex: local.source_object_hash.clone(),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!("cached Router Key local output parse failed: {e}")),
|
||||
detail: Some(format!(
|
||||
"cached Router Key local output parse failed: {e}"
|
||||
)),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1830,9 +1973,10 @@ fn parse_vcir_aspa_output(local: &VcirLocalOutput) -> Result<AspaAttestation, St
|
||||
fn parse_vcir_router_key_output(local: &VcirLocalOutput) -> Result<RouterKeyPayload, String> {
|
||||
let payload: VcirRouterKeyPayload = serde_json::from_str(&local.payload_json)
|
||||
.map_err(|e| format!("invalid Router Key payload JSON: {e}"))?;
|
||||
let ski = hex::decode(&payload.ski_hex)
|
||||
.map_err(|e| format!("invalid Router Key SKI hex: {e}"))?;
|
||||
let spki_der = base64::engine::general_purpose::STANDARD.decode(&payload.spki_der_base64)
|
||||
let ski =
|
||||
hex::decode(&payload.ski_hex).map_err(|e| format!("invalid Router Key SKI hex: {e}"))?;
|
||||
let spki_der = base64::engine::general_purpose::STANDARD
|
||||
.decode(&payload.spki_der_base64)
|
||||
.map_err(|e| format!("invalid Router Key SPKI base64: {e}"))?;
|
||||
Ok(RouterKeyPayload {
|
||||
as_id: payload.as_id,
|
||||
@@ -2255,7 +2399,8 @@ fn build_router_key_local_outputs(
|
||||
.iter()
|
||||
.map(|router_key| {
|
||||
let ski_hex = hex::encode(&router_key.ski);
|
||||
let spki_der_base64 = base64::engine::general_purpose::STANDARD.encode(&router_key.spki_der);
|
||||
let spki_der_base64 =
|
||||
base64::engine::general_purpose::STANDARD.encode(&router_key.spki_der);
|
||||
let rule_hash = sha256_hex(
|
||||
format!(
|
||||
"router-key-rule:{}:{}:{}:{}",
|
||||
@@ -2587,7 +2732,6 @@ fn audit_result_to_vcir_status(result: &AuditObjectResult) -> VcirArtifactValida
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn roa_to_vrps_for_vcir(roa: &RoaObject) -> Vec<Vrp> {
|
||||
let asn = roa.roa.as_id;
|
||||
let mut out = Vec::new();
|
||||
@@ -2629,10 +2773,10 @@ mod tests {
|
||||
use crate::fetch::rsync::LocalDirRsyncFetcher;
|
||||
use crate::fetch::rsync::{RsyncFetchError, RsyncFetcher};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta,
|
||||
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
|
||||
VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirOutputType, VcirRelatedArtifact,
|
||||
VcirSummary,
|
||||
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult,
|
||||
ValidatedManifestMeta, VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus,
|
||||
VcirAuditSummary, VcirChildEntry, VcirInstanceGate, VcirLocalOutput, VcirOutputType,
|
||||
VcirRelatedArtifact, VcirSummary,
|
||||
};
|
||||
use crate::sync::rrdp::Fetcher;
|
||||
use crate::validation::publication_point::PublicationPointSnapshot;
|
||||
@@ -2845,8 +2989,6 @@ authorityKeyIdentifier = keyid:always
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct GeneratedRouter {
|
||||
issuer_ca_der: Vec<u8>,
|
||||
router_der: Vec<u8>,
|
||||
@@ -3045,15 +3187,16 @@ authorityKeyIdentifier = keyid:always
|
||||
}
|
||||
}
|
||||
|
||||
fn cernet_publication_point_snapshot_for_vcir_tests(
|
||||
) -> (PublicationPointSnapshot, Vec<u8>, time::OffsetDateTime) {
|
||||
fn cernet_publication_point_snapshot_for_vcir_tests()
|
||||
-> (PublicationPointSnapshot, Vec<u8>, time::OffsetDateTime) {
|
||||
let dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0");
|
||||
let rsync_base_uri = "rsync://rpki.cernet.net/repo/cernet/0/";
|
||||
let manifest_file = "05FC9C5B88506F7C0D3F862C8895BED67E9F8EBA.mft";
|
||||
let manifest_rsync_uri = format!("{rsync_base_uri}{manifest_file}");
|
||||
let manifest_bytes = std::fs::read(dir.join(manifest_file)).expect("read manifest fixture");
|
||||
let manifest = ManifestObject::decode_der(&manifest_bytes).expect("decode manifest fixture");
|
||||
let manifest =
|
||||
ManifestObject::decode_der(&manifest_bytes).expect("decode manifest fixture");
|
||||
let candidate = manifest.manifest.this_update + time::Duration::seconds(60);
|
||||
let validation_time = if candidate < manifest.manifest.next_update {
|
||||
candidate
|
||||
@@ -3320,7 +3463,7 @@ authorityKeyIdentifier = keyid:always
|
||||
&crate::validation::objects::ObjectsOutput {
|
||||
vrps: Vec::new(),
|
||||
aspas: Vec::new(),
|
||||
router_keys: Vec::new(),
|
||||
router_keys: Vec::new(),
|
||||
local_outputs_cache: cached.clone(),
|
||||
warnings: Vec::new(),
|
||||
stats: crate::validation::objects::ObjectsStats::default(),
|
||||
@@ -3333,22 +3476,32 @@ authorityKeyIdentifier = keyid:always
|
||||
|
||||
#[test]
|
||||
fn collect_and_persist_vcir_embedded_evidence_for_real_signed_objects() {
|
||||
let (pack, issuer_ca_der, validation_time) = cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let (pack, issuer_ca_der, validation_time) =
|
||||
cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca");
|
||||
let objects = crate::validation::objects::process_publication_point_snapshot_for_issuer(
|
||||
&pack,
|
||||
&Policy::default(),
|
||||
issuer_ca_der.as_slice(),
|
||||
Some("rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"),
|
||||
Some(
|
||||
"rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
|
||||
),
|
||||
issuer_ca.tbs.extensions.ip_resources.as_ref(),
|
||||
issuer_ca.tbs.extensions.as_resources.as_ref(),
|
||||
validation_time,
|
||||
None,
|
||||
);
|
||||
assert!(!objects.local_outputs_cache.is_empty(), "expected local outputs from signed objects");
|
||||
assert!(
|
||||
!objects.local_outputs_cache.is_empty(),
|
||||
"expected local outputs from signed objects"
|
||||
);
|
||||
|
||||
let evidence = collect_vcir_embedded_evidence(&pack, &objects).expect("collect embedded evidence");
|
||||
assert!(evidence.len() >= 2, "expected manifest EE and signed-object EE evidence");
|
||||
let evidence =
|
||||
collect_vcir_embedded_evidence(&pack, &objects).expect("collect embedded evidence");
|
||||
assert!(
|
||||
evidence.len() >= 2,
|
||||
"expected manifest EE and signed-object EE evidence"
|
||||
);
|
||||
|
||||
let store_dir = tempfile::tempdir().expect("store dir");
|
||||
let store = RocksStore::open(store_dir.path()).expect("open rocksdb");
|
||||
@@ -3375,15 +3528,19 @@ authorityKeyIdentifier = keyid:always
|
||||
.get_raw_by_hash_entry(&issuer_hash)
|
||||
.expect("load issuer raw entry")
|
||||
.expect("issuer raw entry present");
|
||||
assert!(issuer_entry
|
||||
.origin_uris
|
||||
.iter()
|
||||
.any(|uri| uri.ends_with("BfycW4hQb3wNP4YsiJW-1n6fjro.cer")));
|
||||
assert!(
|
||||
issuer_entry
|
||||
.origin_uris
|
||||
.iter()
|
||||
.any(|uri| uri.ends_with("BfycW4hQb3wNP4YsiJW-1n6fjro.cer"))
|
||||
);
|
||||
for entry in &evidence {
|
||||
assert!(store
|
||||
.get_raw_by_hash_entry(&entry.raw_entry.sha256_hex)
|
||||
.expect("load evidence raw entry")
|
||||
.is_some());
|
||||
assert!(
|
||||
store
|
||||
.get_raw_by_hash_entry(&entry.raw_entry.sha256_hex)
|
||||
.expect("load evidence raw entry")
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3411,7 +3568,9 @@ authorityKeyIdentifier = keyid:always
|
||||
source_object_uri: "rsync://example.test/repo/issuer/router.cer".to_string(),
|
||||
source_object_hash: "11".repeat(32),
|
||||
source_ee_cert_hash: "11".repeat(32),
|
||||
item_effective_until: PackTime { rfc3339_utc: "2026-12-31T00:00:00Z".to_string() },
|
||||
item_effective_until: PackTime {
|
||||
rfc3339_utc: "2026-12-31T00:00:00Z".to_string(),
|
||||
},
|
||||
}],
|
||||
);
|
||||
assert_eq!(outputs.len(), 1);
|
||||
@@ -3422,13 +3581,16 @@ authorityKeyIdentifier = keyid:always
|
||||
|
||||
#[test]
|
||||
fn build_vcir_local_outputs_falls_back_to_decoding_accepted_objects_when_cache_is_empty() {
|
||||
let (pack, issuer_ca_der, validation_time) = cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let (pack, issuer_ca_der, validation_time) =
|
||||
cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca");
|
||||
let objects = crate::validation::objects::process_publication_point_snapshot_for_issuer(
|
||||
&pack,
|
||||
&Policy::default(),
|
||||
issuer_ca_der.as_slice(),
|
||||
Some("rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"),
|
||||
Some(
|
||||
"rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
|
||||
),
|
||||
issuer_ca.tbs.extensions.ip_resources.as_ref(),
|
||||
issuer_ca.tbs.extensions.as_resources.as_ref(),
|
||||
validation_time,
|
||||
@@ -3455,20 +3617,25 @@ authorityKeyIdentifier = keyid:always
|
||||
.expect("rebuild vcir local outputs");
|
||||
assert!(!local_outputs.is_empty());
|
||||
assert_eq!(local_outputs.len(), objects.vrps.len());
|
||||
assert!(local_outputs
|
||||
.iter()
|
||||
.all(|output| output.output_type == VcirOutputType::Vrp));
|
||||
assert!(
|
||||
local_outputs
|
||||
.iter()
|
||||
.all(|output| output.output_type == VcirOutputType::Vrp)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persist_vcir_for_fresh_result_stores_vcir_and_audit_indexes_for_real_snapshot() {
|
||||
let (pack, issuer_ca_der, validation_time) = cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let (pack, issuer_ca_der, validation_time) =
|
||||
cernet_publication_point_snapshot_for_vcir_tests();
|
||||
let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca");
|
||||
let objects = crate::validation::objects::process_publication_point_snapshot_for_issuer(
|
||||
&pack,
|
||||
&Policy::default(),
|
||||
issuer_ca_der.as_slice(),
|
||||
Some("rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"),
|
||||
Some(
|
||||
"rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer",
|
||||
),
|
||||
issuer_ca.tbs.extensions.ip_resources.as_ref(),
|
||||
issuer_ca.tbs.extensions.as_resources.as_ref(),
|
||||
validation_time,
|
||||
@@ -3492,17 +3659,8 @@ authorityKeyIdentifier = keyid:always
|
||||
rrdp_notification_uri: None,
|
||||
};
|
||||
|
||||
persist_vcir_for_fresh_result(
|
||||
&store,
|
||||
&ca,
|
||||
&pack,
|
||||
&objects,
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
validation_time,
|
||||
)
|
||||
.expect("persist vcir for fresh result");
|
||||
persist_vcir_for_fresh_result(&store, &ca, &pack, &objects, &[], &[], &[], validation_time)
|
||||
.expect("persist vcir for fresh result");
|
||||
|
||||
let vcir = store
|
||||
.get_vcir(&pack.manifest_rsync_uri)
|
||||
@@ -3511,10 +3669,15 @@ authorityKeyIdentifier = keyid:always
|
||||
assert_eq!(vcir.manifest_rsync_uri, pack.manifest_rsync_uri);
|
||||
assert_eq!(vcir.summary.local_vrp_count as usize, objects.vrps.len());
|
||||
let first_output = vcir.local_outputs.first().expect("local outputs stored");
|
||||
assert!(store
|
||||
.get_audit_rule_index_entry(crate::storage::AuditRuleKind::Roa, &first_output.rule_hash)
|
||||
.expect("get audit rule index entry")
|
||||
.is_some());
|
||||
assert!(
|
||||
store
|
||||
.get_audit_rule_index_entry(
|
||||
crate::storage::AuditRuleKind::Roa,
|
||||
&first_output.rule_hash
|
||||
)
|
||||
.expect("get audit rule index entry")
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3574,7 +3737,9 @@ authorityKeyIdentifier = keyid:always
|
||||
tal_id: "test-tal".to_string(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
ca_certificate_der: vec![0x11, 0x22],
|
||||
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
||||
ca_certificate_rsync_uri: Some(
|
||||
"rsync://example.test/repo/issuer/issuer.cer".to_string(),
|
||||
),
|
||||
effective_ip_resources: None,
|
||||
effective_as_resources: None,
|
||||
rsync_base_uri: pack.publication_point_rsync_uri.clone(),
|
||||
@@ -3625,21 +3790,37 @@ authorityKeyIdentifier = keyid:always
|
||||
&[],
|
||||
&embedded,
|
||||
);
|
||||
assert!(artifacts.iter().any(|artifact| artifact.artifact_role == VcirArtifactRole::Manifest));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.artifact_role == VcirArtifactRole::TrustAnchorCert));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/issuer.crl")
|
||||
assert!(
|
||||
artifacts
|
||||
.iter()
|
||||
.any(|artifact| artifact.artifact_role == VcirArtifactRole::Manifest)
|
||||
);
|
||||
assert!(
|
||||
artifacts
|
||||
.iter()
|
||||
.any(|artifact| artifact.artifact_role == VcirArtifactRole::TrustAnchorCert)
|
||||
);
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/issuer.crl")
|
||||
&& artifact.artifact_role == VcirArtifactRole::CurrentCrl));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/child.cer")
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/child.cer")
|
||||
&& artifact.artifact_role == VcirArtifactRole::ChildCaCert));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/a.roa")
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/a.roa")
|
||||
&& artifact.validation_status == VcirArtifactValidationStatus::Rejected));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/a.asa")
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/a.asa")
|
||||
&& artifact.validation_status == VcirArtifactValidationStatus::WarningOnly));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/a.gbr")
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/a.gbr")
|
||||
&& artifact.artifact_kind == VcirArtifactKind::Gbr));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref() == Some("rsync://example.test/repo/issuer/extra.bin")
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
|
||||
== Some("rsync://example.test/repo/issuer/extra.bin")
|
||||
&& artifact.artifact_kind == VcirArtifactKind::Other));
|
||||
assert!(artifacts.iter().any(|artifact| artifact.uri.is_none() && artifact.sha256 == sha256_hex(b"embedded-ee")));
|
||||
assert!(artifacts.iter().any(
|
||||
|artifact| artifact.uri.is_none() && artifact.sha256 == sha256_hex(b"embedded-ee")
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4130,7 +4311,11 @@ authorityKeyIdentifier = keyid:always
|
||||
PublicationPointSource::Fresh | PublicationPointSource::VcirCurrentInstance
|
||||
));
|
||||
|
||||
assert_eq!(calls.load(Ordering::SeqCst), 1, "module-scope dedup should skip second sync");
|
||||
assert_eq!(
|
||||
calls.load(Ordering::SeqCst),
|
||||
1,
|
||||
"module-scope dedup should skip second sync"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4385,12 +4570,15 @@ authorityKeyIdentifier = keyid:always
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&pp.snapshot,
|
||||
&[],
|
||||
&objects,
|
||||
&[],
|
||||
);
|
||||
assert_eq!(audit.source, "vcir_current_instance");
|
||||
assert_eq!(audit.repo_sync_phase, None);
|
||||
assert_eq!(audit.repo_terminal_state, "fallback_current_instance");
|
||||
assert!(
|
||||
audit
|
||||
.objects
|
||||
@@ -4453,15 +4641,21 @@ authorityKeyIdentifier = keyid:always
|
||||
let audit = build_publication_point_audit_from_snapshot(
|
||||
&issuer,
|
||||
pp.source,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some("rsync"),
|
||||
Some("rsync_only_ok"),
|
||||
Some(123),
|
||||
Some("none"),
|
||||
&pp.snapshot,
|
||||
&[],
|
||||
&objects,
|
||||
&[],
|
||||
);
|
||||
assert_eq!(audit.objects[0].kind, AuditObjectKind::Manifest);
|
||||
assert_eq!(audit.repo_sync_source.as_deref(), Some("rsync"));
|
||||
assert_eq!(audit.repo_sync_phase.as_deref(), Some("rsync_only_ok"));
|
||||
assert_eq!(audit.repo_sync_duration_ms, Some(123));
|
||||
assert_eq!(audit.repo_sync_error.as_deref(), Some("none"));
|
||||
assert_eq!(audit.repo_terminal_state, "fresh");
|
||||
|
||||
let crl = audit
|
||||
.objects
|
||||
@@ -4484,7 +4678,6 @@ authorityKeyIdentifier = keyid:always
|
||||
let _ = now;
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn discover_children_with_router_certificate_records_ok_audit_and_no_child() {
|
||||
let g = generate_router_cert_with_variant("ec-p256", true);
|
||||
@@ -4505,7 +4698,9 @@ authorityKeyIdentifier = keyid:always
|
||||
tal_id: "test-tal".to_string(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
ca_certificate_der: g.issuer_ca_der.clone(),
|
||||
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
||||
ca_certificate_rsync_uri: Some(
|
||||
"rsync://example.test/repo/issuer/issuer.cer".to_string(),
|
||||
),
|
||||
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
||||
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
||||
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||
@@ -4524,11 +4719,13 @@ authorityKeyIdentifier = keyid:always
|
||||
assert!(out.children.is_empty());
|
||||
assert_eq!(out.audits.len(), 1);
|
||||
assert!(matches!(out.audits[0].result, AuditObjectResult::Ok));
|
||||
assert!(out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("validated BGPsec router certificate"));
|
||||
assert!(
|
||||
out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("validated BGPsec router certificate")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4551,7 +4748,9 @@ authorityKeyIdentifier = keyid:always
|
||||
tal_id: "test-tal".to_string(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
ca_certificate_der: g.issuer_ca_der.clone(),
|
||||
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
||||
ca_certificate_rsync_uri: Some(
|
||||
"rsync://example.test/repo/issuer/issuer.cer".to_string(),
|
||||
),
|
||||
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
||||
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
||||
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||
@@ -4570,11 +4769,13 @@ authorityKeyIdentifier = keyid:always
|
||||
assert!(out.children.is_empty());
|
||||
assert_eq!(out.audits.len(), 1);
|
||||
assert!(matches!(out.audits[0].result, AuditObjectResult::Skipped));
|
||||
assert!(out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("not a CA resource certificate or BGPsec router certificate"));
|
||||
assert!(
|
||||
out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("not a CA resource certificate or BGPsec router certificate")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4597,7 +4798,9 @@ authorityKeyIdentifier = keyid:always
|
||||
tal_id: "test-tal".to_string(),
|
||||
parent_manifest_rsync_uri: None,
|
||||
ca_certificate_der: g.issuer_ca_der.clone(),
|
||||
ca_certificate_rsync_uri: Some("rsync://example.test/repo/issuer/issuer.cer".to_string()),
|
||||
ca_certificate_rsync_uri: Some(
|
||||
"rsync://example.test/repo/issuer/issuer.cer".to_string(),
|
||||
),
|
||||
effective_ip_resources: issuer_ca.tbs.extensions.ip_resources.clone(),
|
||||
effective_as_resources: issuer_ca.tbs.extensions.as_resources.clone(),
|
||||
rsync_base_uri: "rsync://example.test/repo/issuer/".to_string(),
|
||||
@@ -4616,11 +4819,13 @@ authorityKeyIdentifier = keyid:always
|
||||
assert!(out.children.is_empty());
|
||||
assert_eq!(out.audits.len(), 1);
|
||||
assert!(matches!(out.audits[0].result, AuditObjectResult::Error));
|
||||
assert!(out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("router certificate validation failed"));
|
||||
assert!(
|
||||
out.audits[0]
|
||||
.detail
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
.contains("router certificate validation failed")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5210,9 +5415,10 @@ authorityKeyIdentifier = keyid:always
|
||||
let audit = build_publication_point_audit_from_vcir(
|
||||
&ca,
|
||||
PublicationPointSource::VcirCurrentInstance,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some("rsync"),
|
||||
Some("rrdp_failed_rsync_failed"),
|
||||
Some(456),
|
||||
Some("rsync failed"),
|
||||
Some(&vcir),
|
||||
None,
|
||||
&runner_warnings,
|
||||
@@ -5221,6 +5427,14 @@ authorityKeyIdentifier = keyid:always
|
||||
);
|
||||
|
||||
assert_eq!(audit.source, "vcir_current_instance");
|
||||
assert_eq!(audit.repo_sync_source.as_deref(), Some("rsync"));
|
||||
assert_eq!(
|
||||
audit.repo_sync_phase.as_deref(),
|
||||
Some("rrdp_failed_rsync_failed")
|
||||
);
|
||||
assert_eq!(audit.repo_sync_duration_ms, Some(456));
|
||||
assert_eq!(audit.repo_sync_error.as_deref(), Some("rsync failed"));
|
||||
assert_eq!(audit.repo_terminal_state, "fallback_current_instance");
|
||||
assert_eq!(audit.objects[0].rsync_uri, vcir.current_manifest_rsync_uri);
|
||||
assert_eq!(audit.objects[0].kind, AuditObjectKind::Manifest);
|
||||
assert_eq!(
|
||||
@@ -5270,16 +5484,17 @@ authorityKeyIdentifier = keyid:always
|
||||
let audit = build_publication_point_audit_from_vcir(
|
||||
&ca,
|
||||
PublicationPointSource::FailedFetchNoCache,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some("rsync"),
|
||||
Some("rsync_only_failed"),
|
||||
Some(789),
|
||||
Some("load from network failed, fallback to cache"),
|
||||
None,
|
||||
None,
|
||||
&[Warning::new("runner warning")],
|
||||
&crate::validation::objects::ObjectsOutput {
|
||||
vrps: Vec::new(),
|
||||
aspas: Vec::new(),
|
||||
router_keys: Vec::new(),
|
||||
router_keys: Vec::new(),
|
||||
local_outputs_cache: Vec::new(),
|
||||
warnings: vec![Warning::new("object warning")],
|
||||
stats: crate::validation::objects::ObjectsStats::default(),
|
||||
@@ -5289,6 +5504,14 @@ authorityKeyIdentifier = keyid:always
|
||||
);
|
||||
|
||||
assert_eq!(audit.source, "failed_fetch_no_cache");
|
||||
assert_eq!(audit.repo_sync_source.as_deref(), Some("rsync"));
|
||||
assert_eq!(audit.repo_sync_phase.as_deref(), Some("rsync_only_failed"));
|
||||
assert_eq!(audit.repo_sync_duration_ms, Some(789));
|
||||
assert_eq!(
|
||||
audit.repo_sync_error.as_deref(),
|
||||
Some("load from network failed, fallback to cache")
|
||||
);
|
||||
assert_eq!(audit.repo_terminal_state, "failed_no_cache");
|
||||
assert!(audit.this_update_rfc3339_utc.is_empty());
|
||||
assert!(audit.next_update_rfc3339_utc.is_empty());
|
||||
assert!(audit.verified_at_rfc3339_utc.is_empty());
|
||||
|
||||
Reference in New Issue
Block a user