20260326 完成数据库model迁移;20260327 增加一键replay脚本
This commit is contained in:
@@ -7,7 +7,9 @@ use rpki::data_model::rc::{
|
||||
use rpki::data_model::roa::RoaObject;
|
||||
|
||||
use rpki::storage::PackFile;
|
||||
use rpki::storage::RawByHashEntry;
|
||||
use rpki::storage::RocksStore;
|
||||
use sha2::Digest;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
@@ -304,7 +306,12 @@ fn landing_packfile_cbor_put(store: &RocksStore, obj_type: ObjType, sample: &str
|
||||
let pf = PackFile::from_bytes_compute_sha256(rsync_uri, bytes.to_vec());
|
||||
let encoded = serde_cbor::to_vec(std::hint::black_box(&pf)).expect("cbor encode packfile");
|
||||
let key = format!("bench:packfile:{}:{}", obj_type.as_str(), sample);
|
||||
store.put_raw(&key, &encoded).expect("store put_raw");
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&encoded));
|
||||
let mut entry = RawByHashEntry::from_bytes(sha256_hex, encoded);
|
||||
entry.origin_uris.push(key);
|
||||
entry.object_type = Some("cbor".to_string());
|
||||
entry.encoding = Some("cbor".to_string());
|
||||
store.put_raw_by_hash_entry(&entry).expect("store raw_by_hash");
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
@@ -560,7 +567,7 @@ fn stage2_decode_validate_and_landing_benchmark_selected_der_v2() {
|
||||
}
|
||||
|
||||
if mode.do_landing() {
|
||||
// Landing benchmark: PackFile(from_bytes_compute_sha256) + CBOR + RocksDB put_raw.
|
||||
// Landing benchmark: PackFile(from_bytes_compute_sha256) + CBOR + RocksDB current-state landing.
|
||||
let mut per_round_ns_per_op = Vec::with_capacity(rounds as usize);
|
||||
for _ in 0..warmup_iters {
|
||||
landing_packfile_cbor_put(&store, s.obj_type, &s.name, bytes.as_slice());
|
||||
@@ -617,7 +624,7 @@ fn stage2_decode_validate_and_landing_benchmark_selected_der_v2() {
|
||||
println!();
|
||||
}
|
||||
if mode.do_landing() {
|
||||
println!("## landing (PackFile::from_bytes_compute_sha256 + CBOR + RocksDB put_raw)");
|
||||
println!("## landing (PackFile::from_bytes_compute_sha256 + CBOR + RocksDB current-state landing)");
|
||||
println!();
|
||||
println!("| type | sample | size_bytes | complexity | avg ns/op | ops/s |");
|
||||
println!("|---|---|---:|---:|---:|---:|");
|
||||
@@ -693,7 +700,7 @@ fn stage2_decode_validate_and_landing_benchmark_selected_der_v2() {
|
||||
|
||||
if let Some(path) = out_md_landing {
|
||||
let md = render_markdown(
|
||||
"Stage2 landing benchmark (PackFile CBOR + RocksDB put_raw)",
|
||||
"Stage2 landing benchmark (PackFile CBOR + RocksDB current-state landing)",
|
||||
&landing_rows,
|
||||
);
|
||||
write_text_file(&path, &md);
|
||||
|
||||
@@ -168,18 +168,17 @@ fn apnic_live_delta_only_from_persistent_db() {
|
||||
.as_deref()
|
||||
.expect("APNIC root must have rrdpNotification");
|
||||
|
||||
let state_bytes = store
|
||||
.get_rrdp_state(rrdp_notification_uri)
|
||||
.expect("get rrdp_state")
|
||||
let source = store
|
||||
.get_rrdp_source_record(rrdp_notification_uri)
|
||||
.expect("get rrdp source")
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"missing rrdp_state for APNIC notification URI; run bootstrap test first. db_dir={}",
|
||||
"missing RRDP source state for APNIC notification URI; run bootstrap test first. db_dir={}",
|
||||
db_dir.display()
|
||||
)
|
||||
});
|
||||
let state = rpki::sync::rrdp::RrdpState::decode(&state_bytes).expect("decode rrdp_state");
|
||||
let old_serial = state.serial;
|
||||
let old_session = state.session_id;
|
||||
let old_serial = source.last_serial.expect("last serial");
|
||||
let old_session = source.last_session_id.expect("last session");
|
||||
|
||||
let max_wait_secs: u64 = std::env::var("RPKI_LIVE_MAX_WAIT_SECS")
|
||||
.ok()
|
||||
|
||||
@@ -212,11 +212,12 @@ fn apnic_tree_full_stats_serial() {
|
||||
|
||||
let mut raw_total = 0usize;
|
||||
let mut raw_by_ext: BTreeMap<String, usize> = BTreeMap::new();
|
||||
for (k, _v) in store.raw_iter_all().expect("raw_iter_all") {
|
||||
for entry in store
|
||||
.list_repository_view_entries_with_prefix("rsync://")
|
||||
.expect("list repository view")
|
||||
{
|
||||
raw_total += 1;
|
||||
if let Ok(uri) = std::str::from_utf8(&k) {
|
||||
*raw_by_ext.entry(ext_of_uri(uri)).or_insert(0) += 1;
|
||||
}
|
||||
*raw_by_ext.entry(ext_of_uri(&entry.rsync_uri)).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
println!("APNIC Stage2 full-tree serial stats");
|
||||
@@ -270,7 +271,7 @@ fn apnic_tree_full_stats_serial() {
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
"rocksdb_raw_objects_total={} raw_by_ext={:?}",
|
||||
"current_repository_view_total={} by_ext={:?}",
|
||||
raw_total, raw_by_ext
|
||||
);
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ use sha2::Digest;
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::policy::{CaFailedFetchPolicy, Policy};
|
||||
use rpki::storage::{
|
||||
PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta,
|
||||
PackTime, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
|
||||
ValidatedCaInstanceResult, ValidatedManifestMeta,
|
||||
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
|
||||
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
|
||||
};
|
||||
@@ -115,6 +116,26 @@ fn store_validated_manifest_baseline(
|
||||
.expect("store validated manifest baseline");
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_success_returns_validated_publication_point_data() {
|
||||
let manifest_path = Path::new(
|
||||
@@ -131,9 +152,7 @@ fn manifest_success_returns_validated_publication_point_data() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -146,7 +165,7 @@ fn manifest_success_returns_validated_publication_point_data() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
@@ -186,9 +205,7 @@ fn manifest_hash_mismatch_reuses_current_instance_vcir_when_enabled() {
|
||||
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -201,7 +218,7 @@ fn manifest_hash_mismatch_reuses_current_instance_vcir_when_enabled() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
@@ -234,11 +251,11 @@ fn manifest_hash_mismatch_reuses_current_instance_vcir_when_enabled() {
|
||||
let victim = entries.first().expect("non-empty file list");
|
||||
let victim_uri = format!("{publication_point_rsync_uri}{}", victim.file_name);
|
||||
let mut tampered = store
|
||||
.get_raw(&victim_uri)
|
||||
.expect("get victim raw")
|
||||
.load_current_object_bytes_by_uri(&victim_uri)
|
||||
.expect("load victim raw")
|
||||
.expect("victim raw exists");
|
||||
tampered[0] ^= 0xFF;
|
||||
store.put_raw(&victim_uri, &tampered).expect("tamper raw");
|
||||
put_current_object(&store, &victim_uri, tampered, victim_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
|
||||
let second = process_manifest_publication_point(
|
||||
&store,
|
||||
@@ -274,9 +291,7 @@ fn manifest_failed_fetch_stop_all_output() {
|
||||
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -289,7 +304,7 @@ fn manifest_failed_fetch_stop_all_output() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let mut policy = Policy::default();
|
||||
@@ -313,11 +328,11 @@ fn manifest_failed_fetch_stop_all_output() {
|
||||
let victim = entries.first().expect("non-empty file list");
|
||||
let victim_uri = format!("{publication_point_rsync_uri}{}", victim.file_name);
|
||||
let mut tampered = store
|
||||
.get_raw(&victim_uri)
|
||||
.expect("get victim raw")
|
||||
.load_current_object_bytes_by_uri(&victim_uri)
|
||||
.expect("load victim raw")
|
||||
.expect("victim raw exists");
|
||||
tampered[0] ^= 0xFF;
|
||||
store.put_raw(&victim_uri, &tampered).expect("tamper raw");
|
||||
put_current_object(&store, &victim_uri, tampered, victim_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
|
||||
policy.ca_failed_fetch_policy = CaFailedFetchPolicy::StopAllOutput;
|
||||
let err = process_manifest_publication_point(
|
||||
@@ -350,9 +365,7 @@ fn manifest_failed_fetch_rejects_stale_current_instance_vcir() {
|
||||
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -365,7 +378,7 @@ fn manifest_failed_fetch_rejects_stale_current_instance_vcir() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
@@ -391,8 +404,16 @@ fn manifest_failed_fetch_rejects_stale_current_instance_vcir() {
|
||||
);
|
||||
|
||||
store
|
||||
.delete_raw(&manifest_rsync_uri)
|
||||
.expect("delete manifest raw to force fallback");
|
||||
.delete_raw_by_hash_entry(
|
||||
store
|
||||
.get_repository_view_entry(&manifest_rsync_uri)
|
||||
.expect("get repository view")
|
||||
.expect("manifest repository view exists")
|
||||
.current_hash
|
||||
.as_deref()
|
||||
.expect("manifest current hash"),
|
||||
)
|
||||
.expect("delete manifest raw_by_hash to force missing current object");
|
||||
|
||||
let err = process_manifest_publication_point(
|
||||
&store,
|
||||
@@ -425,9 +446,7 @@ fn manifest_revalidation_with_unchanged_manifest_is_fresh() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -440,7 +459,7 @@ fn manifest_revalidation_with_unchanged_manifest_is_fresh() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
@@ -507,9 +526,7 @@ fn manifest_rollback_is_treated_as_failed_fetch_and_reuses_current_instance_vcir
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -522,7 +539,7 @@ fn manifest_rollback_is_treated_as_failed_fetch_and_reuses_current_instance_vcir
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::policy::Policy;
|
||||
use rpki::storage::RocksStore;
|
||||
use rpki::storage::{RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore};
|
||||
use rpki::validation::manifest::{
|
||||
ManifestProcessError, PublicationPointSource, process_manifest_publication_point,
|
||||
};
|
||||
@@ -40,6 +42,26 @@ fn fixture_dir_to_rsync_uri(dir: &Path) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_outside_publication_point_yields_no_usable_cache() {
|
||||
let manifest_path = Path::new(
|
||||
@@ -56,9 +78,7 @@ fn manifest_outside_publication_point_yields_no_usable_cache() {
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
// Store manifest and its locked files so Fresh would otherwise succeed.
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
for entry in manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -71,7 +91,12 @@ fn manifest_outside_publication_point_yields_no_usable_cache() {
|
||||
.join(entry.file_name.as_str());
|
||||
let bytes = std::fs::read(&file_path).expect("read referenced file");
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(
|
||||
&store,
|
||||
&rsync_uri,
|
||||
bytes,
|
||||
rsync_uri.rsplit('.').next().unwrap_or("bin"),
|
||||
);
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
@@ -112,9 +137,7 @@ fn manifest_outside_publication_point_detects_current_instance_snapshot_pp_misma
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
for entry in manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -127,7 +150,12 @@ fn manifest_outside_publication_point_detects_current_instance_snapshot_pp_misma
|
||||
.join(entry.file_name.as_str());
|
||||
let bytes = std::fs::read(&file_path).expect("read referenced file");
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(
|
||||
&store,
|
||||
&rsync_uri,
|
||||
bytes,
|
||||
rsync_uri.rsplit('.').next().unwrap_or("bin"),
|
||||
);
|
||||
}
|
||||
|
||||
let policy = Policy::default();
|
||||
|
||||
@@ -5,7 +5,8 @@ use sha2::Digest;
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::policy::{CaFailedFetchPolicy, Policy};
|
||||
use rpki::storage::{
|
||||
PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult, ValidatedManifestMeta,
|
||||
PackTime, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
|
||||
ValidatedCaInstanceResult, ValidatedManifestMeta,
|
||||
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
|
||||
VcirInstanceGate, VcirRelatedArtifact, VcirSummary,
|
||||
};
|
||||
@@ -118,6 +119,26 @@ fn store_validated_manifest_baseline(
|
||||
.expect("store validated manifest baseline");
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
fn store_manifest_and_locked_files(
|
||||
store: &RocksStore,
|
||||
manifest_path: &Path,
|
||||
@@ -126,9 +147,7 @@ fn store_manifest_and_locked_files(
|
||||
manifest: &ManifestObject,
|
||||
manifest_bytes: &[u8],
|
||||
) {
|
||||
store
|
||||
.put_raw(manifest_rsync_uri, manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(store, manifest_rsync_uri, manifest_bytes.to_vec(), "mft");
|
||||
for entry in manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -142,7 +161,7 @@ fn store_manifest_and_locked_files(
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
put_current_object(&store, &rsync_uri, bytes, rsync_uri.rsplit('.').next().unwrap_or("bin"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,9 +277,7 @@ fn manifest_missing_locked_file_is_treated_as_failed_fetch() {
|
||||
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest only (no locked files)");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
|
||||
let issuer_ca_der = issuer_ca_fixture_der();
|
||||
let policy = Policy::default();
|
||||
@@ -279,7 +296,8 @@ fn manifest_missing_locked_file_is_treated_as_failed_fetch() {
|
||||
"{err}"
|
||||
);
|
||||
assert!(
|
||||
err.to_string().contains("file missing in raw_objects"),
|
||||
err.to_string()
|
||||
.contains("file missing in current repository view"),
|
||||
"unexpected error: {err}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::policy::{CaFailedFetchPolicy, Policy};
|
||||
use rpki::storage::RocksStore;
|
||||
use rpki::storage::{RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore};
|
||||
use rpki::validation::manifest::process_manifest_publication_point;
|
||||
|
||||
fn issuer_ca_fixture() -> Vec<u8> {
|
||||
@@ -16,6 +18,26 @@ fn issuer_ca_rsync_uri() -> &'static str {
|
||||
"rsync://rpki.apnic.net/repository/B527EF581D6611E2BB468F7C72FD1FF2/BfycW4hQb3wNP4YsiJW-1n6fjro.cer"
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_outside_publication_point_is_failed_fetch_rfc9286_section6_1() {
|
||||
let fixture_manifest_path = Path::new(
|
||||
@@ -35,9 +57,7 @@ fn manifest_outside_publication_point_is_failed_fetch_rfc9286_section6_1() {
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
// Store the manifest at its rsync URI.
|
||||
store
|
||||
.put_raw(manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest raw");
|
||||
put_current_object(&store, manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
|
||||
// Store all referenced files under the (different) publication point so that §6.4/§6.5
|
||||
// would otherwise succeed if §6.1 was not enforced.
|
||||
@@ -50,7 +70,8 @@ fn manifest_outside_publication_point_is_failed_fetch_rfc9286_section6_1() {
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file raw");
|
||||
let object_type = rsync_uri.rsplit('.').next().unwrap_or("bin");
|
||||
put_current_object(&store, &rsync_uri, bytes, object_type);
|
||||
}
|
||||
|
||||
let mut policy = Policy::default();
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use rpki::data_model::crl::RpkixCrl;
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::data_model::rc::ResourceCertificate;
|
||||
use rpki::policy::{Policy, SignedObjectFailurePolicy};
|
||||
use rpki::storage::{PackFile, RocksStore};
|
||||
use rpki::storage::{
|
||||
PackFile, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
|
||||
};
|
||||
use rpki::validation::manifest::process_manifest_publication_point;
|
||||
use rpki::validation::objects::process_publication_point_snapshot_for_issuer;
|
||||
|
||||
@@ -30,6 +34,26 @@ fn fixture_dir_to_rsync_uri(dir: &Path) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
fn build_cernet_pack_and_validation_time() -> (
|
||||
rpki::validation::publication_point::PublicationPointSnapshot,
|
||||
time::OffsetDateTime,
|
||||
@@ -48,9 +72,7 @@ fn build_cernet_pack_and_validation_time() -> (
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -63,7 +85,8 @@ fn build_cernet_pack_and_validation_time() -> (
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
let object_type = rsync_uri.rsplit('.').next().unwrap_or("bin");
|
||||
put_current_object(&store, &rsync_uri, bytes, object_type);
|
||||
}
|
||||
|
||||
let issuer_ca_der = std::fs::read(
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use rpki::data_model::crl::RpkixCrl;
|
||||
use rpki::data_model::manifest::ManifestObject;
|
||||
use rpki::data_model::rc::ResourceCertificate;
|
||||
use rpki::policy::{Policy, SignedObjectFailurePolicy};
|
||||
use rpki::storage::{PackFile, RocksStore};
|
||||
use rpki::storage::{
|
||||
PackFile, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
|
||||
};
|
||||
use rpki::validation::manifest::process_manifest_publication_point;
|
||||
use rpki::validation::objects::process_publication_point_snapshot_for_issuer;
|
||||
|
||||
@@ -30,6 +34,26 @@ fn fixture_dir_to_rsync_uri(dir: &Path) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(&bytes));
|
||||
let mut raw_entry = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes);
|
||||
raw_entry.origin_uris.push(rsync_uri.to_string());
|
||||
raw_entry.object_type = Some(object_type.to_string());
|
||||
raw_entry.encoding = Some("der".to_string());
|
||||
store
|
||||
.put_raw_by_hash_entry(&raw_entry)
|
||||
.expect("store raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("store repository view");
|
||||
}
|
||||
|
||||
fn build_cernet_pack_and_validation_time() -> (
|
||||
rpki::validation::publication_point::PublicationPointSnapshot,
|
||||
time::OffsetDateTime,
|
||||
@@ -48,9 +72,7 @@ fn build_cernet_pack_and_validation_time() -> (
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw(&manifest_rsync_uri, &manifest_bytes)
|
||||
.expect("store manifest");
|
||||
put_current_object(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
|
||||
let entries = manifest
|
||||
.manifest
|
||||
.parse_files()
|
||||
@@ -63,7 +85,8 @@ fn build_cernet_pack_and_validation_time() -> (
|
||||
let bytes = std::fs::read(&file_path)
|
||||
.unwrap_or_else(|_| panic!("read fixture file referenced by manifest: {file_path:?}"));
|
||||
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
|
||||
store.put_raw(&rsync_uri, &bytes).expect("store file");
|
||||
let object_type = rsync_uri.rsplit('.').next().unwrap_or("bin");
|
||||
put_current_object(&store, &rsync_uri, bytes, object_type);
|
||||
}
|
||||
|
||||
let issuer_ca_der = std::fs::read(
|
||||
|
||||
@@ -95,7 +95,7 @@ fn build_publication_point_snapshot_from_local_rsync_fixture(
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.expect("sync into raw_objects");
|
||||
.expect("sync into current repository view");
|
||||
|
||||
let pp = process_manifest_publication_point(
|
||||
&store,
|
||||
|
||||
@@ -20,6 +20,15 @@ impl Fetcher for MapFetcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_current_object(store: &RocksStore, uri: &str, expected: &[u8]) {
|
||||
assert_eq!(
|
||||
store
|
||||
.load_current_object_bytes_by_uri(uri)
|
||||
.expect("load current object"),
|
||||
Some(expected.to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
struct CountingRsyncFetcher<F> {
|
||||
inner: F,
|
||||
calls: Arc<Mutex<usize>>,
|
||||
@@ -92,13 +101,7 @@ fn repo_sync_uses_rrdp_when_available() {
|
||||
assert_eq!(out.objects_written, 2);
|
||||
assert_eq!(*calls.lock().unwrap(), 0);
|
||||
|
||||
assert_eq!(
|
||||
store
|
||||
.get_raw("rsync://example.net/repo/obj1.cer")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"abc"
|
||||
);
|
||||
assert_current_object(&store, "rsync://example.net/repo/obj1.cer", b"abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -166,13 +169,7 @@ fn repo_sync_skips_snapshot_when_state_unchanged() {
|
||||
"expected no rsync fallback calls"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
store
|
||||
.get_raw("rsync://example.net/repo/obj1.cer")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"abc"
|
||||
);
|
||||
assert_current_object(&store, "rsync://example.net/repo/obj1.cer", b"abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -221,17 +218,11 @@ fn repo_sync_falls_back_to_rsync_on_rrdp_failure() {
|
||||
.any(|r| r.0 == "RFC 8182 §3.4.5")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
store
|
||||
.get_raw("rsync://example.net/repo/sub/obj.cer")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"hello"
|
||||
);
|
||||
assert_current_object(&store, "rsync://example.net/repo/sub/obj.cer", b"hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repo_sync_rsync_populates_raw_objects() {
|
||||
fn repo_sync_rsync_populates_current_repository_view() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
@@ -263,18 +254,6 @@ fn repo_sync_rsync_populates_raw_objects() {
|
||||
assert_eq!(out.source, RepoSyncSource::Rsync);
|
||||
assert_eq!(out.objects_written, 2);
|
||||
|
||||
assert_eq!(
|
||||
store
|
||||
.get_raw("rsync://example.net/repo/a/one.cer")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"1"
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.get_raw("rsync://example.net/repo/a/b/two.crl")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"2"
|
||||
);
|
||||
assert_current_object(&store, "rsync://example.net/repo/a/one.cer", b"1");
|
||||
assert_current_object(&store, "rsync://example.net/repo/a/b/two.crl", b"2");
|
||||
}
|
||||
|
||||
@@ -17,6 +17,15 @@ impl Fetcher for MapFetcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_current_object(store: &RocksStore, uri: &str, expected: &[u8]) {
|
||||
assert_eq!(
|
||||
store
|
||||
.load_current_object_bytes_by_uri(uri)
|
||||
.expect("load current object"),
|
||||
Some(expected.to_vec())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notification_parses_and_snapshot_is_applied_and_state_written() {
|
||||
let notification_xml =
|
||||
@@ -47,28 +56,18 @@ fn notification_parses_and_snapshot_is_applied_and_state_written() {
|
||||
.expect("sync");
|
||||
assert_eq!(published, 2);
|
||||
|
||||
let obj1 = store
|
||||
.get_raw("rsync://example.net/repo/obj1.cer")
|
||||
.expect("get obj1")
|
||||
.expect("obj1 exists");
|
||||
assert_eq!(obj1, b"abc");
|
||||
assert_current_object(&store, "rsync://example.net/repo/obj1.cer", b"abc");
|
||||
assert_current_object(&store, "rsync://example.net/repo/obj2.crl", b"def");
|
||||
|
||||
let obj2 = store
|
||||
.get_raw("rsync://example.net/repo/obj2.crl")
|
||||
.expect("get obj2")
|
||||
.expect("obj2 exists");
|
||||
assert_eq!(obj2, b"def");
|
||||
|
||||
let state_bytes = store
|
||||
.get_rrdp_state("https://example.net/rrdp/notification.xml")
|
||||
.expect("get state")
|
||||
.expect("state exists");
|
||||
let state = rpki::sync::rrdp::RrdpState::decode(&state_bytes).expect("decode state");
|
||||
let source = store
|
||||
.get_rrdp_source_record("https://example.net/rrdp/notification.xml")
|
||||
.expect("get source")
|
||||
.expect("source exists");
|
||||
assert_eq!(
|
||||
state.session_id,
|
||||
source.last_session_id.expect("session id"),
|
||||
"9df4b597-af9e-4dca-bdda-719cce2c4e28".to_string()
|
||||
);
|
||||
assert_eq!(state.serial, 1);
|
||||
assert_eq!(source.last_serial, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2,7 +2,8 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rpki::storage::RocksStore;
|
||||
use rpki::storage::{RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore};
|
||||
use sha2::Digest;
|
||||
|
||||
const RSYNC_BASE_URI: &str = "rsync://rpki.luys.cloud/repo/LY-RPKI/1/";
|
||||
|
||||
@@ -90,7 +91,31 @@ fn rsync_fallback_breakdown_luys_cloud() {
|
||||
|
||||
let t3 = Instant::now();
|
||||
for (uri, bytes) in &objects {
|
||||
store.put_raw(uri, bytes).expect("put_raw");
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(bytes));
|
||||
let mut raw = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes.clone());
|
||||
raw.origin_uris.push(uri.clone());
|
||||
raw.object_type = Some(
|
||||
uri.rsplit('.')
|
||||
.next()
|
||||
.unwrap_or("bin")
|
||||
.to_ascii_lowercase(),
|
||||
);
|
||||
raw.encoding = Some("der".to_string());
|
||||
store.put_raw_by_hash_entry(&raw).expect("put raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: uri.clone(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some(rsync_base_uri.clone()),
|
||||
object_type: Some(
|
||||
uri.rsplit('.')
|
||||
.next()
|
||||
.unwrap_or("bin")
|
||||
.to_ascii_lowercase(),
|
||||
),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("put repository view");
|
||||
}
|
||||
let write_wall = t3.elapsed();
|
||||
|
||||
@@ -114,7 +139,7 @@ fn rsync_fallback_breakdown_luys_cloud() {
|
||||
pct(read_wall)
|
||||
);
|
||||
println!(
|
||||
"| rocksdb_put_raw | {:>11.3} | {:>5.1}% | write raw_objects ({} keys) |",
|
||||
"| rocksdb_put_current | {:>11.3} | {:>5.1}% | write repository_view + raw_by_hash ({} keys) |",
|
||||
write_wall.as_secs_f64() * 1000.0,
|
||||
pct(write_wall),
|
||||
objects.len()
|
||||
|
||||
@@ -1,21 +1,37 @@
|
||||
use rpki::storage::RocksStore;
|
||||
use rpki::storage::{RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore};
|
||||
use sha2::Digest;
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: &[u8], object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(bytes));
|
||||
let mut raw = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes.to_vec());
|
||||
raw.origin_uris.push(rsync_uri.to_string());
|
||||
raw.object_type = Some(object_type.to_string());
|
||||
raw.encoding = Some("der".to_string());
|
||||
store.put_raw_by_hash_entry(&raw).expect("put raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.test/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("put repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_iter_all_lists_raw_entries() {
|
||||
fn storage_iter_all_lists_repository_view_entries() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw("rsync://example.test/repo/a.cer", b"a")
|
||||
.expect("put_raw a");
|
||||
store
|
||||
.put_raw("rsync://example.test/repo/b.roa", b"b")
|
||||
.expect("put_raw b");
|
||||
put_current_object(&store, "rsync://example.test/repo/a.cer", b"a", "cer");
|
||||
put_current_object(&store, "rsync://example.test/repo/b.roa", b"b", "roa");
|
||||
|
||||
let raw_keys = store
|
||||
.raw_iter_all()
|
||||
.expect("raw_iter_all")
|
||||
.map(|(k, _v)| String::from_utf8(k.to_vec()).expect("utf8 key"))
|
||||
.list_repository_view_entries_with_prefix("rsync://")
|
||||
.expect("list repository_view")
|
||||
.into_iter()
|
||||
.map(|entry| entry.rsync_uri)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(raw_keys.len(), 2);
|
||||
assert!(raw_keys.contains(&"rsync://example.test/repo/a.cer".to_string()));
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
use rocksdb::WriteBatch;
|
||||
|
||||
use rpki::storage::RocksStore;
|
||||
|
||||
#[test]
|
||||
fn storage_delete_rrdp_state_works() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
use rpki::storage::{RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore};
|
||||
use sha2::Digest;
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: &[u8], object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(bytes));
|
||||
let mut raw = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes.to_vec());
|
||||
raw.origin_uris.push(rsync_uri.to_string());
|
||||
raw.object_type = Some(object_type.to_string());
|
||||
raw.encoding = Some("der".to_string());
|
||||
store.put_raw_by_hash_entry(&raw).expect("put raw_by_hash");
|
||||
store
|
||||
.put_rrdp_state("https://example.net/rrdp/notification.xml", b"state")
|
||||
.expect("put state");
|
||||
assert_eq!(
|
||||
store
|
||||
.get_rrdp_state("https://example.net/rrdp/notification.xml")
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
b"state"
|
||||
);
|
||||
|
||||
store
|
||||
.delete_rrdp_state("https://example.net/rrdp/notification.xml")
|
||||
.expect("delete state");
|
||||
assert!(
|
||||
store
|
||||
.get_rrdp_state("https://example.net/rrdp/notification.xml")
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.net/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("put repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -34,25 +26,20 @@ fn storage_raw_iter_prefix_filters_by_prefix() {
|
||||
let temp = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
||||
|
||||
store
|
||||
.put_raw("rsync://example.net/repo/a/1.cer", b"1")
|
||||
.unwrap();
|
||||
store
|
||||
.put_raw("rsync://example.net/repo/a/2.cer", b"2")
|
||||
.unwrap();
|
||||
store
|
||||
.put_raw("rsync://example.net/repo/b/1.cer", b"3")
|
||||
.unwrap();
|
||||
put_current_object(&store, "rsync://example.net/repo/a/1.cer", b"1", "cer");
|
||||
put_current_object(&store, "rsync://example.net/repo/a/2.cer", b"2", "cer");
|
||||
put_current_object(&store, "rsync://example.net/repo/b/1.cer", b"3", "cer");
|
||||
|
||||
let prefix = b"rsync://example.net/repo/a/";
|
||||
let prefix = "rsync://example.net/repo/a/";
|
||||
let items = store
|
||||
.raw_iter_prefix(prefix)
|
||||
.list_repository_view_entries_with_prefix(prefix)
|
||||
.expect("iter")
|
||||
.map(|(k, v)| (String::from_utf8_lossy(&k).to_string(), v.to_vec()))
|
||||
.into_iter()
|
||||
.map(|entry| entry.rsync_uri)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(items.len(), 2);
|
||||
for (k, _v) in &items {
|
||||
for k in &items {
|
||||
assert!(k.starts_with("rsync://example.net/repo/a/"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
use std::path::Path;
|
||||
|
||||
use rpki::storage::RocksStore;
|
||||
use rpki::storage::{
|
||||
PackTime, RawByHashEntry, RepositoryViewEntry, RepositoryViewState, RocksStore,
|
||||
RrdpSourceRecord, RrdpSourceSyncState,
|
||||
};
|
||||
use sha2::Digest;
|
||||
|
||||
fn put_current_object(store: &RocksStore, rsync_uri: &str, bytes: &[u8], object_type: &str) {
|
||||
let sha256_hex = hex::encode(sha2::Sha256::digest(bytes));
|
||||
let mut raw = RawByHashEntry::from_bytes(sha256_hex.clone(), bytes.to_vec());
|
||||
raw.origin_uris.push(rsync_uri.to_string());
|
||||
raw.object_type = Some(object_type.to_string());
|
||||
raw.encoding = Some("der".to_string());
|
||||
store.put_raw_by_hash_entry(&raw).expect("put raw_by_hash");
|
||||
store
|
||||
.put_repository_view_entry(&RepositoryViewEntry {
|
||||
rsync_uri: rsync_uri.to_string(),
|
||||
current_hash: Some(sha256_hex),
|
||||
repository_source: Some("https://example.invalid/notification.xml".to_string()),
|
||||
object_type: Some(object_type.to_string()),
|
||||
state: RepositoryViewState::Present,
|
||||
})
|
||||
.expect("put repository view");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_opens_and_creates_column_families() {
|
||||
@@ -9,32 +31,65 @@ fn storage_opens_and_creates_column_families() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_objects_roundtrip_by_rsync_uri() {
|
||||
fn current_object_roundtrip_by_rsync_uri() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(dir.path()).expect("open rocksdb");
|
||||
|
||||
let key = "rsync://example.invalid/repo/a.cer";
|
||||
let value = b"hello";
|
||||
store.put_raw(key, value).expect("put raw");
|
||||
let got = store.get_raw(key).expect("get raw");
|
||||
put_current_object(&store, key, value, "cer");
|
||||
let got = store
|
||||
.load_current_object_bytes_by_uri(key)
|
||||
.expect("get current object");
|
||||
assert_eq!(got.as_deref(), Some(value.as_slice()));
|
||||
|
||||
store.delete_raw(key).expect("delete raw");
|
||||
let got = store.get_raw(key).expect("get raw after delete");
|
||||
let current_hash = store
|
||||
.get_repository_view_entry(key)
|
||||
.expect("get repository view")
|
||||
.expect("view exists")
|
||||
.current_hash
|
||||
.expect("current hash");
|
||||
store
|
||||
.delete_repository_view_entry(key)
|
||||
.expect("delete repository view");
|
||||
store
|
||||
.delete_raw_by_hash_entry(¤t_hash)
|
||||
.expect("delete raw_by_hash");
|
||||
let got = store
|
||||
.load_current_object_bytes_by_uri(key)
|
||||
.expect("get current object after delete");
|
||||
assert!(got.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rrdp_state_roundtrip_by_notification_uri() {
|
||||
fn rrdp_source_roundtrip_by_notification_uri() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(dir.path()).expect("open rocksdb");
|
||||
|
||||
let notif = "https://example.invalid/rrdp/notification.xml";
|
||||
let state = b"{\"session_id\":\"00000000-0000-0000-0000-000000000000\",\"last_serial\":1}";
|
||||
store.put_rrdp_state(notif, state).expect("put rrdp_state");
|
||||
let record = RrdpSourceRecord {
|
||||
notify_uri: notif.to_string(),
|
||||
last_session_id: Some("00000000-0000-0000-0000-000000000000".to_string()),
|
||||
last_serial: Some(1),
|
||||
first_seen_at: PackTime::from_utc_offset_datetime(time::OffsetDateTime::now_utc()),
|
||||
last_seen_at: PackTime::from_utc_offset_datetime(time::OffsetDateTime::now_utc()),
|
||||
last_sync_at: None,
|
||||
sync_state: RrdpSourceSyncState::SnapshotOnly,
|
||||
last_snapshot_uri: None,
|
||||
last_snapshot_hash: None,
|
||||
last_error: None,
|
||||
};
|
||||
store.put_rrdp_source_record(&record).expect("put rrdp_source");
|
||||
|
||||
let got = store.get_rrdp_state(notif).expect("get rrdp_state");
|
||||
assert_eq!(got.as_deref(), Some(state.as_slice()));
|
||||
let got = store
|
||||
.get_rrdp_source_record(notif)
|
||||
.expect("get rrdp_source")
|
||||
.expect("rrdp_source present");
|
||||
assert_eq!(got.last_serial, Some(1));
|
||||
assert_eq!(
|
||||
got.last_session_id.as_deref(),
|
||||
Some("00000000-0000-0000-0000-000000000000")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -43,14 +98,12 @@ fn store_is_reopenable() {
|
||||
let path: &Path = dir.path();
|
||||
|
||||
let store = RocksStore::open(path).expect("open rocksdb");
|
||||
store
|
||||
.put_raw("rsync://example.invalid/repo/x", b"x")
|
||||
.expect("put");
|
||||
put_current_object(&store, "rsync://example.invalid/repo/x", b"x", "bin");
|
||||
drop(store);
|
||||
|
||||
let store = RocksStore::open(path).expect("reopen rocksdb");
|
||||
let got = store
|
||||
.get_raw("rsync://example.invalid/repo/x")
|
||||
.load_current_object_bytes_by_uri("rsync://example.invalid/repo/x")
|
||||
.expect("get after reopen");
|
||||
assert_eq!(got.as_deref(), Some(b"x".as_slice()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user