Skip to content

Commit 137b56c

Browse files
authored
Merge pull request #1138 from input-output-hk/jpraynaud/1137-fix-snapshot-archive
Add snapshot archive verification
2 parents 9a1e80d + 5da42ac commit 137b56c

File tree

4 files changed

+71
-6
lines changed

4 files changed

+71
-6
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.3.66"
3+
version = "0.3.67"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/services/signed_entity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl SignedEntityService for MithrilSignedEntityService {
120120
"certificate_hash" => &certificate.hash
121121
);
122122

123-
let mut remaining_retries = 3;
123+
let mut remaining_retries = 2;
124124
let artifact = loop {
125125
remaining_retries -= 1;
126126

mithril-aggregator/src/snapshotter.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use flate2::write::GzEncoder;
21
use flate2::Compression;
2+
use flate2::{read::GzDecoder, write::GzEncoder};
33
use mithril_common::StdResult;
44
use slog_scope::{info, warn};
55
use std::error::Error as StdError;
66
use std::fs::File;
7-
use std::io;
7+
use std::io::{self, Seek, SeekFrom};
88
use std::path::{Path, PathBuf};
99
use std::sync::RwLock;
10+
use tar::Archive;
1011
use thiserror::Error;
1112

1213
use crate::dependency_injection::DependenciesBuilderError;
@@ -53,6 +54,10 @@ pub enum SnapshotError {
5354
#[error("Create archive error: {0}")]
5455
CreateArchiveError(#[from] io::Error),
5556

57+
/// Set when the snapshotter creates an invalid snapshot.
58+
#[error("Invalid archive error: {0}")]
59+
InvalidArchiveError(String),
60+
5661
/// Set when the snapshotter fails at uploading the snapshot.
5762
#[error("Upload file error: `{0}`")]
5863
UploadFileError(String),
@@ -65,7 +70,7 @@ pub enum SnapshotError {
6570
impl Snapshotter for GzipSnapshotter {
6671
fn snapshot(&self, archive_name: &str) -> Result<OngoingSnapshot, SnapshotError> {
6772
let archive_path = self.ongoing_snapshot_directory.join(archive_name);
68-
let filesize = self.create_archive(&archive_path).map_err(|err| {
73+
let filesize = self.create_and_verify_archive(&archive_path).map_err(|err| {
6974
if archive_path.exists() {
7075
if let Err(remove_error) = std::fs::remove_file(&archive_path) {
7176
warn!(
@@ -142,6 +147,32 @@ impl GzipSnapshotter {
142147

143148
Ok(filesize)
144149
}
150+
151+
fn create_and_verify_archive(&self, archive_path: &Path) -> Result<u64, SnapshotError> {
152+
let filesize = self.create_archive(archive_path)?;
153+
self.verify_archive(archive_path)?;
154+
155+
Ok(filesize)
156+
}
157+
158+
// Verify if an archive is corrupted (i.e. at least one entry is invalid)
159+
fn verify_archive(&self, archive_path: &Path) -> Result<(), SnapshotError> {
160+
info!("verifying archive: {}", archive_path.display());
161+
162+
let mut snapshot_file_tar_gz = File::open(archive_path)
163+
.map_err(|e| SnapshotError::InvalidArchiveError(e.to_string()))?;
164+
165+
snapshot_file_tar_gz.seek(SeekFrom::Start(0))?;
166+
let snapshot_file_tar = GzDecoder::new(snapshot_file_tar_gz);
167+
let mut snapshot_archive = Archive::new(snapshot_file_tar);
168+
169+
match snapshot_archive.entries()?.find(|e| e.is_err()) {
170+
Some(Err(e)) => Err(SnapshotError::InvalidArchiveError(format!(
171+
"invalid entry with error: '{e:?}'"
172+
))),
173+
_ => Ok(()),
174+
}
175+
}
145176
}
146177

147178
/// Snapshotter that does nothing. It is mainly used for test purposes.
@@ -198,6 +229,8 @@ impl Snapshotter for DumbSnapshotter {
198229
mod tests {
199230
use std::sync::Arc;
200231

232+
use mithril_common::digesters::DummyImmutablesDbBuilder;
233+
201234
use super::*;
202235

203236
fn get_test_directory(dir_name: &str) -> PathBuf {
@@ -289,4 +322,36 @@ mod tests {
289322

290323
assert_eq!(vec!["other-process.file".to_string()], remaining_files);
291324
}
325+
326+
#[test]
327+
fn should_create_a_valid_archive_with_gzip_snapshotter() {
328+
let test_dir = get_test_directory("should_create_a_valid_archive_with_gzip_snapshotter");
329+
let pending_snapshot_directory = test_dir.join("pending_snapshot");
330+
let pending_snapshot_archive_file = "archive.tar.gz";
331+
let db_directory = test_dir.join("db");
332+
333+
DummyImmutablesDbBuilder::new(db_directory.as_os_str().to_str().unwrap())
334+
.with_immutables(&[1, 2, 3])
335+
.append_immutable_trio()
336+
.build();
337+
338+
let snapshotter = Arc::new(
339+
GzipSnapshotter::new(db_directory, pending_snapshot_directory.clone()).unwrap(),
340+
);
341+
342+
snapshotter
343+
.create_archive(
344+
&pending_snapshot_directory.join(Path::new(pending_snapshot_archive_file)),
345+
)
346+
.expect("create_archive should not fail");
347+
snapshotter
348+
.verify_archive(
349+
&pending_snapshot_directory.join(Path::new(pending_snapshot_archive_file)),
350+
)
351+
.expect("verify_archive should not fail");
352+
353+
snapshotter
354+
.snapshot(pending_snapshot_archive_file)
355+
.expect("Snapshotter::snapshot should not fail.");
356+
}
292357
}

0 commit comments

Comments
 (0)