111 lines
3.6 KiB
Rust
111 lines
3.6 KiB
Rust
use crate::fetch::rsync::{RsyncFetchError, RsyncFetcher};
|
|
use crate::policy::{Policy, SyncPreference};
|
|
use crate::report::{RfcRef, Warning};
|
|
use crate::storage::RocksStore;
|
|
use crate::sync::rrdp::{
|
|
Fetcher as HttpFetcher, RrdpSyncError, sync_from_notification,
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum RepoSyncSource {
|
|
Rrdp,
|
|
Rsync,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct RepoSyncResult {
|
|
pub source: RepoSyncSource,
|
|
pub objects_written: usize,
|
|
pub warnings: Vec<Warning>,
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum RepoSyncError {
|
|
#[error("RRDP sync failed: {0}")]
|
|
Rrdp(#[from] RrdpSyncError),
|
|
|
|
#[error("rsync fallback failed: {0}")]
|
|
Rsync(#[from] RsyncFetchError),
|
|
|
|
#[error("storage error: {0}")]
|
|
Storage(String),
|
|
}
|
|
|
|
/// Sync a publication point into `raw_objects`.
|
|
///
|
|
/// v1 behavior:
|
|
/// - If `rrdp_notification_uri` is present and `policy.sync_preference` is `rrdp_then_rsync`,
|
|
/// try RRDP snapshot sync first (RFC 8182 §3.4.1-§3.4.3).
|
|
/// - On RRDP failure, fall back to rsync (RFC 8182 §3.4.5).
|
|
/// - If `sync_preference` is `rsync_only` or there is no RRDP URI, use rsync.
|
|
pub fn sync_publication_point(
|
|
store: &RocksStore,
|
|
policy: &Policy,
|
|
rrdp_notification_uri: Option<&str>,
|
|
rsync_base_uri: &str,
|
|
http_fetcher: &dyn HttpFetcher,
|
|
rsync_fetcher: &dyn RsyncFetcher,
|
|
) -> Result<RepoSyncResult, RepoSyncError> {
|
|
match (policy.sync_preference, rrdp_notification_uri) {
|
|
(SyncPreference::RrdpThenRsync, Some(notification_uri)) => {
|
|
match try_rrdp_sync(store, notification_uri, http_fetcher) {
|
|
Ok(written) => Ok(RepoSyncResult {
|
|
source: RepoSyncSource::Rrdp,
|
|
objects_written: written,
|
|
warnings: Vec::new(),
|
|
}),
|
|
Err(err) => {
|
|
let warnings = vec![
|
|
Warning::new(format!("RRDP failed; falling back to rsync: {err}"))
|
|
.with_rfc_refs(&[RfcRef("RFC 8182 §3.4.5")])
|
|
.with_context(notification_uri),
|
|
];
|
|
let written =
|
|
rsync_sync_into_raw_objects(store, rsync_base_uri, rsync_fetcher)?;
|
|
Ok(RepoSyncResult {
|
|
source: RepoSyncSource::Rsync,
|
|
objects_written: written,
|
|
warnings,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
let written = rsync_sync_into_raw_objects(store, rsync_base_uri, rsync_fetcher)?;
|
|
Ok(RepoSyncResult {
|
|
source: RepoSyncSource::Rsync,
|
|
objects_written: written,
|
|
warnings: Vec::new(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
fn try_rrdp_sync(
|
|
store: &RocksStore,
|
|
notification_uri: &str,
|
|
http_fetcher: &dyn HttpFetcher,
|
|
) -> Result<usize, RrdpSyncError> {
|
|
let notification_xml = http_fetcher
|
|
.fetch(notification_uri)
|
|
.map_err(RrdpSyncError::Fetch)?;
|
|
|
|
sync_from_notification(store, notification_uri, ¬ification_xml, http_fetcher)
|
|
}
|
|
|
|
fn rsync_sync_into_raw_objects(
|
|
store: &RocksStore,
|
|
rsync_base_uri: &str,
|
|
rsync_fetcher: &dyn RsyncFetcher,
|
|
) -> Result<usize, RepoSyncError> {
|
|
let objects = rsync_fetcher.fetch_objects(rsync_base_uri)?;
|
|
let mut written = 0usize;
|
|
for (rsync_uri, bytes) in objects {
|
|
store
|
|
.put_raw(&rsync_uri, &bytes)
|
|
.map_err(|e| RepoSyncError::Storage(e.to_string()))?;
|
|
written += 1;
|
|
}
|
|
Ok(written)
|
|
}
|