diff --git a/upki/src/revocation/fetch.rs b/upki/src/revocation/fetch.rs index 0d5df543..30075f13 100644 --- a/upki/src/revocation/fetch.rs +++ b/upki/src/revocation/fetch.rs @@ -75,7 +75,14 @@ pub async fn fetch(dry_run: bool, config: &Config) -> Result { manifest.introduce()?; - let plan = Plan::construct(&manifest, &config.revocation.fetch_url, &cache_dir)?; + let old_manifest = Manifest::from_config(config).ok(); + + let plan = Plan::construct( + &old_manifest, + &manifest, + &config.revocation.fetch_url, + &cache_dir, + )?; if dry_run { println!( @@ -110,10 +117,12 @@ pub(crate) struct Plan { impl Plan { /// Form a plan of how to synchronize with the remote server. /// + /// - `old_manifest` is an alleged current manifest, whose files are left alone. /// - `manifest` describes the contents of the remote server. /// - `remote_url` is the base URL. /// - `local` is the path into which files are downloaded. The caller ensures this exists. pub(crate) fn construct( + old_manifest: &Option, manifest: &Manifest, remote_url: &str, local: &Path, @@ -157,6 +166,12 @@ impl Plan { steps.push(PlanStep::download(filter, remote_url, local)); } + if let Some(old_manifest) = &old_manifest { + for filter in &old_manifest.filters { + unwanted_files.remove(Path::new(&filter.filename)); + } + } + steps.push(PlanStep::SaveManifest { manifest: manifest.clone(), local_dir: local.to_owned(), diff --git a/upki/src/revocation/mod.rs b/upki/src/revocation/mod.rs index 8794014e..2916a98d 100644 --- a/upki/src/revocation/mod.rs +++ b/upki/src/revocation/mod.rs @@ -111,7 +111,7 @@ impl Manifest { /// This performs disk IO but does not perform network IO. pub fn verify(&self, config: &Config) -> Result { self.introduce()?; - let plan = Plan::construct(self, "https://.../", &config.revocation_cache_dir())?; + let plan = Plan::construct(&None, self, "https://.../", &config.revocation_cache_dir())?; match plan.download_bytes() { 0 => Ok(ExitCode::SUCCESS), bytes => Err(Error::Outdated(bytes)), diff --git a/upki/tests/integration.rs b/upki/tests/integration.rs index 97e4c487..40cec7f3 100644 --- a/upki/tests/integration.rs +++ b/upki/tests/integration.rs @@ -260,7 +260,38 @@ fn full_fetch_and_incremental_update() { GET /manifest.json -> 200 OK (547 bytes) GET /filter4.delta -> 200 OK (3 bytes) "); - // filter2 is deleted, filter4 is new + // filter2 could be deleted, filter4 is new + assert_eq!( + list_dir(&temp.path().join("revocation")), + vec![ + "filter1.filter", + "filter2.delta", + "filter3.delta", + "filter4.delta", + "manifest.json" + ] + ); + + // a fetch of the same manifest clears away "filter2.delta" as it is now unused + let (server, _filters) = http_server("tests/data/evolution/"); + write_config(&temp, server.url()); + assert_cmd_snapshot!( + upki() + .arg("--config-file") + .arg(&config_file) + .arg("fetch"), + @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + "); + assert_snapshot!( + server.into_log(), + @"GET /manifest.json -> 200 OK (547 bytes)"); + + // filter2 is now deleted assert_eq!( list_dir(&temp.path().join("revocation")), vec![