rpki/tests/test_tree_failure_handling.rs
2026-02-10 12:09:59 +08:00

159 lines
5.4 KiB
Rust

use std::collections::HashMap;
use rpki::audit::{DiscoveredFrom, PublicationPointAudit};
use rpki::report::Warning;
use rpki::storage::{PackTime, VerifiedPublicationPointPack};
use rpki::validation::manifest::PublicationPointSource;
use rpki::validation::objects::{ObjectsOutput, ObjectsStats};
use rpki::validation::tree::{
CaInstanceHandle, DiscoveredChildCaInstance, PublicationPointRunResult, PublicationPointRunner,
TreeRunConfig, run_tree_serial,
};
fn empty_pack(manifest_uri: &str, pp_uri: &str) -> VerifiedPublicationPointPack {
VerifiedPublicationPointPack {
format_version: VerifiedPublicationPointPack::FORMAT_VERSION_V1,
publication_point_rsync_uri: pp_uri.to_string(),
manifest_rsync_uri: manifest_uri.to_string(),
this_update: PackTime {
rfc3339_utc: "2026-01-01T00:00:00Z".to_string(),
},
next_update: PackTime {
rfc3339_utc: "2026-12-31T00:00:00Z".to_string(),
},
verified_at: PackTime {
rfc3339_utc: "2026-02-06T00:00:00Z".to_string(),
},
manifest_bytes: vec![1, 2, 3],
files: Vec::new(),
}
}
fn ca_handle(manifest_uri: &str) -> CaInstanceHandle {
CaInstanceHandle {
depth: 0,
ca_certificate_der: Vec::new(),
ca_certificate_rsync_uri: None,
effective_ip_resources: None,
effective_as_resources: None,
rsync_base_uri: "rsync://example.test/repo/".to_string(),
manifest_rsync_uri: manifest_uri.to_string(),
publication_point_rsync_uri: "rsync://example.test/repo/".to_string(),
rrdp_notification_uri: None,
}
}
fn discovered_child(
parent_manifest_uri: &str,
child_manifest_uri: &str,
) -> DiscoveredChildCaInstance {
let name = child_manifest_uri
.rsplit('/')
.next()
.unwrap_or("child.mft")
.trim_end_matches(".mft");
DiscoveredChildCaInstance {
handle: ca_handle(child_manifest_uri),
discovered_from: DiscoveredFrom {
parent_manifest_rsync_uri: parent_manifest_uri.to_string(),
child_ca_certificate_rsync_uri: format!("rsync://example.test/repo/{name}.cer"),
child_ca_certificate_sha256_hex: "00".repeat(32),
},
}
}
#[derive(Default)]
struct ResultRunner {
by_manifest: HashMap<String, Result<PublicationPointRunResult, String>>,
}
impl ResultRunner {
fn with_ok(mut self, manifest: &str, res: PublicationPointRunResult) -> Self {
self.by_manifest.insert(manifest.to_string(), Ok(res));
self
}
fn with_err(mut self, manifest: &str, err: &str) -> Self {
self.by_manifest
.insert(manifest.to_string(), Err(err.to_string()));
self
}
}
impl PublicationPointRunner for ResultRunner {
fn run_publication_point(
&self,
ca: &CaInstanceHandle,
) -> Result<PublicationPointRunResult, String> {
self.by_manifest
.get(&ca.manifest_rsync_uri)
.cloned()
.unwrap_or_else(|| Err(format!("no mock for {}", ca.manifest_rsync_uri)))
}
}
#[test]
fn tree_continues_when_a_publication_point_fails() {
let root_manifest = "rsync://example.test/repo/root.mft";
let bad_child_manifest = "rsync://example.test/repo/bad-child.mft";
let ok_child_manifest = "rsync://example.test/repo/ok-child.mft";
let runner = ResultRunner::default()
.with_ok(
root_manifest,
PublicationPointRunResult {
source: PublicationPointSource::Fresh,
pack: empty_pack(root_manifest, "rsync://example.test/repo/"),
warnings: Vec::new(),
objects: ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
warnings: Vec::new(),
stats: ObjectsStats::default(),
audit: Vec::new(),
},
audit: PublicationPointAudit::default(),
discovered_children: vec![
discovered_child(root_manifest, bad_child_manifest),
discovered_child(root_manifest, ok_child_manifest),
],
},
)
.with_err(bad_child_manifest, "synthetic failure")
.with_ok(
ok_child_manifest,
PublicationPointRunResult {
source: PublicationPointSource::Fresh,
pack: empty_pack(ok_child_manifest, "rsync://example.test/repo/ok-child/"),
warnings: vec![Warning::new("ok child warning")],
objects: ObjectsOutput {
vrps: Vec::new(),
aspas: Vec::new(),
warnings: Vec::new(),
stats: ObjectsStats::default(),
audit: Vec::new(),
},
audit: PublicationPointAudit::default(),
discovered_children: Vec::new(),
},
);
let out = run_tree_serial(ca_handle(root_manifest), &runner, &TreeRunConfig::default())
.expect("run tree");
assert_eq!(out.instances_processed, 2);
assert_eq!(out.instances_failed, 1);
assert!(
out.warnings
.iter()
.any(|w| w.message.contains("publication point failed")),
"expected failure warning"
);
assert!(
out.warnings
.iter()
.any(|w| w.message.contains("ok child warning")),
"expected ok child warning propagated"
);
}