Skip to content

Commit 67c9eff

Browse files
committed
check version regression
1 parent b1e2ca2 commit 67c9eff

File tree

6 files changed

+141
-105
lines changed

6 files changed

+141
-105
lines changed

Cargo.lock

Lines changed: 0 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ flate2 = "1.0.23"
1919
hex = "0.4.3"
2020
mithril-common = { path = "../mithril-common" }
2121
reqwest = { version = "0.11", features = ["json"] }
22-
semver = "1.0"
2322
serde = { version = "1.0", features = ["derive"] }
2423
serde_json = "1.0"
2524
serde_yaml = "0.9.10"

mithril-aggregator/src/command_args.rs

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use chrono::Local;
21
use clap::{Parser, Subcommand};
32
use config::{builder::DefaultState, ConfigBuilder, Map, Source, Value, ValueKind};
4-
use semver::{Version, VersionReq};
53
use slog::Level;
6-
use slog_scope::{debug, warn};
7-
use sqlite::Connection;
4+
use slog_scope::debug;
85
use std::error::Error;
96
use std::fs;
107
use std::path::PathBuf;
@@ -15,9 +12,7 @@ use tokio::time::Duration;
1512
use mithril_common::certificate_chain::MithrilCertificateVerifier;
1613
use mithril_common::chain_observer::{CardanoCliRunner, ChainObserver};
1714
use mithril_common::crypto_helper::ProtocolGenesisVerifier;
18-
use mithril_common::database::{
19-
ApplicationNodeType, ApplicationVersion, VersionProvider, VersionUpdaterProvider,
20-
};
15+
use mithril_common::database::{ApplicationNodeType, ApplicationVersionChecker};
2116
use mithril_common::digesters::{CardanoImmutableDigester, ImmutableFileSystemObserver};
2217
use mithril_common::entities::{Epoch, HexEncodedGenesisSecretKey};
2318
use mithril_common::store::adapter::SQLiteAdapter;
@@ -38,52 +33,16 @@ use crate::{
3833
ProtocolParametersStorer, SingleSignatureStore, VerificationKeyStore,
3934
};
4035

41-
fn check_database_version(filepath: &Option<PathBuf>) -> Result<(), Box<dyn Error>> {
42-
let connection = match filepath {
43-
Some(file) => Connection::open(file)?,
44-
None => Connection::open(":memory:")?,
45-
};
46-
let provider = VersionProvider::new(&connection);
47-
provider.create_table_if_not_exists()?;
48-
let application_type = ApplicationNodeType::new("aggregator")?;
49-
let maybe_option = provider.get_application_version(&application_type)?;
50-
let current_version = ApplicationVersion {
51-
semver: Version::parse(env!("CARGO_PKG_VERSION"))?,
52-
application_type,
53-
updated_at: Local::now().naive_local(),
54-
};
55-
56-
match maybe_option {
57-
None => {
58-
let provider = VersionUpdaterProvider::new(&connection);
59-
let _ = provider.save(current_version)?;
60-
debug!("application version saved in database");
61-
}
62-
Some(version) => {
63-
let req = VersionReq::parse(&current_version.semver.to_string()).unwrap();
64-
65-
if !req.matches(&version.semver) {
66-
warn!(
67-
"application version '{}' is out of date, new version is '{}'. Upgrading database…",
68-
version.semver, current_version.semver
69-
);
70-
let upgrader_provider = VersionUpdaterProvider::new(&connection);
71-
upgrader_provider.save(current_version)?;
72-
debug!("database updated");
73-
} else {
74-
debug!("database up to date");
75-
}
76-
}
77-
};
78-
79-
Ok(())
80-
}
81-
8236
fn setup_genesis_dependencies(
8337
config: &GenesisConfiguration,
8438
) -> Result<GenesisToolsDependency, Box<dyn std::error::Error>> {
8539
let sqlite_db_path = Some(config.get_sqlite_file());
86-
check_database_version(&sqlite_db_path)?;
40+
ApplicationVersionChecker::new(
41+
slog_scope::logger(),
42+
ApplicationNodeType::Aggregator,
43+
config.get_sqlite_file(),
44+
)
45+
.check(env!("CARGO_PKG_VERSION"))?;
8746

8847
let chain_observer = Arc::new(
8948
mithril_common::chain_observer::CardanoCliChainObserver::new(Box::new(
@@ -345,7 +304,12 @@ impl ServeCommand {
345304
.map_err(|e| format!("configuration deserialize error: {}", e))?;
346305
debug!("SERVE command"; "config" => format!("{:?}", config));
347306
let sqlite_db_path = Some(config.get_sqlite_file());
348-
check_database_version(&sqlite_db_path)?;
307+
ApplicationVersionChecker::new(
308+
slog_scope::logger(),
309+
ApplicationNodeType::Aggregator,
310+
config.get_sqlite_file(),
311+
)
312+
.check(env!("CARGO_PKG_VERSION"))?;
349313

350314
// Init dependencies
351315
let snapshot_store = config.build_snapshot_store()?;

mithril-common/src/database/db_version.rs

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use std::{collections::HashMap, error::Error, fmt::Display};
1+
use std::{cmp::Ordering, collections::HashMap, error::Error, fmt::Display, path::PathBuf};
22

3-
use chrono::NaiveDateTime;
3+
use chrono::{Local, NaiveDateTime};
44
use semver::Version;
5+
use slog::{debug, error, warn, Logger};
56
use sqlite::{Connection, Row, Value};
67

78
use crate::sqlite::{HydrationError, Projection, ProjectionField, Provider, SqLiteEntity};
@@ -232,6 +233,90 @@ returning {projection}
232233
}
233234
}
234235

236+
/// Struct to perform application version check in the database.
237+
#[derive(Debug)]
238+
pub struct ApplicationVersionChecker {
239+
/// Pathbuf to the SQLite3 file.
240+
sqlite_file_path: PathBuf,
241+
242+
/// Application type which vesion is verified.
243+
application_type: ApplicationNodeType,
244+
245+
/// logger
246+
logger: Logger,
247+
}
248+
249+
impl ApplicationVersionChecker {
250+
/// constructor
251+
pub fn new(
252+
logger: Logger,
253+
application_type: ApplicationNodeType,
254+
sqlite_file_path: PathBuf,
255+
) -> Self {
256+
Self {
257+
sqlite_file_path,
258+
application_type,
259+
logger,
260+
}
261+
}
262+
263+
/// Performs an actual version check in the database. This method creates a
264+
/// connection to the SQLite3 file and drops it at the end.
265+
pub fn check(&self, current_semver: &str) -> Result<(), Box<dyn Error>> {
266+
debug!(
267+
&self.logger,
268+
"check application version, database file = '{}'",
269+
self.sqlite_file_path.display()
270+
);
271+
let connection = Connection::open(&self.sqlite_file_path)?;
272+
let provider = VersionProvider::new(&connection);
273+
provider.create_table_if_not_exists()?;
274+
let updater = VersionUpdaterProvider::new(&connection);
275+
let maybe_option = provider.get_application_version(&self.application_type)?;
276+
let current_version = ApplicationVersion {
277+
semver: Version::parse(current_semver)?,
278+
application_type: self.application_type.clone(),
279+
updated_at: Local::now().naive_local(),
280+
};
281+
282+
match maybe_option {
283+
None => {
284+
let current_version = updater.save(current_version)?;
285+
debug!(
286+
&self.logger,
287+
"Application version '{}' saved in database.", current_version.semver
288+
);
289+
}
290+
Some(version) => match current_version.semver.cmp(&version.semver) {
291+
Ordering::Greater => {
292+
warn!(
293+
&self.logger,
294+
"Application version '{}' is out of date, new version is '{}'. Upgrading database…",
295+
version.semver, current_version.semver
296+
);
297+
updater.save(current_version)?;
298+
debug!(&self.logger, "database updated");
299+
}
300+
Ordering::Less => {
301+
error!(
302+
&self.logger,
303+
"Software version '{}' is older than database structure version '{}'.",
304+
current_version.semver,
305+
version.semver
306+
);
307+
308+
Err("This software version is older than the database structure. Aborting launch to prevent possible data corruption.")?;
309+
}
310+
Ordering::Equal => {
311+
debug!(&self.logger, "database up to date");
312+
}
313+
},
314+
};
315+
316+
Ok(())
317+
}
318+
}
319+
235320
#[cfg(test)]
236321
mod tests {
237322
use super::*;
@@ -278,4 +363,36 @@ returning app_version.semver as semver, app_version.application_type as applicat
278363
provider.get_definition(None)
279364
)
280365
}
366+
367+
fn check_database_version(filepath: &PathBuf, semver: &str) {
368+
let connection = Connection::open(filepath).unwrap();
369+
let provider = VersionProvider::new(&connection);
370+
let version = provider
371+
.get_application_version(&ApplicationNodeType::Aggregator)
372+
.unwrap()
373+
.expect("there should be a version in the database");
374+
375+
assert_eq!(semver, version.semver.to_string());
376+
}
377+
378+
#[test]
379+
fn test_application_version_checker() {
380+
let filepath = std::env::temp_dir().join("test.sqlite3");
381+
382+
if filepath.exists() {
383+
std::fs::remove_file(filepath.as_path()).unwrap();
384+
}
385+
let app_checker = ApplicationVersionChecker::new(
386+
slog_scope::logger(),
387+
ApplicationNodeType::Aggregator,
388+
filepath.clone(),
389+
);
390+
app_checker.check("1.0.0").unwrap();
391+
check_database_version(&filepath, "1.0.0");
392+
app_checker.check("1.0.0").unwrap();
393+
check_database_version(&filepath, "1.0.0");
394+
app_checker.check("1.1.0").unwrap();
395+
check_database_version(&filepath, "1.1.0");
396+
app_checker.check("1.0.1").unwrap_err();
397+
}
281398
}

mithril-signer/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,19 @@ repository = { workspace = true }
1111

1212
[dependencies]
1313
async-trait = "0.1.52"
14-
chrono = "0.4"
1514
clap = { version = "4.0", features = ["derive", "env"] }
1615
config = "0.13.1"
1716
hex = "0.4.3"
1817
mithril-common = { path = "../mithril-common" }
1918
rand_chacha = "0.3.1"
2019
rand_core = "0.6.3"
2120
reqwest = { version = "0.11", features = ["json", "stream"] }
22-
semver = "1.0"
2321
serde = { version = "1.0", features = ["derive"] }
2422
serde_json = "1.0"
2523
slog = { version = "2.7.0", features = ["max_level_trace", "release_max_level_debug"] }
2624
slog-async = "2.7.0"
2725
slog-bunyan = "2.4.0"
2826
slog-scope = "4.4.0"
29-
sqlite = "0.28.1"
3027
thiserror = "1.0.31"
3128
tokio = { version = "1.17.0", features = ["full"] }
3229

mithril-signer/src/main.rs

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
use chrono::Local;
21
use clap::Parser;
3-
use semver::{Version, VersionReq};
42
use slog::{o, Drain, Level, Logger};
5-
use slog_scope::{debug, warn};
6-
use sqlite::Connection;
3+
use slog_scope::debug;
74
use std::sync::Arc;
85
use std::time::Duration;
96
use std::{error::Error, path::PathBuf};
107

11-
use mithril_common::database::{
12-
ApplicationNodeType, ApplicationVersion, VersionProvider, VersionUpdaterProvider,
13-
};
8+
use mithril_common::database::{ApplicationNodeType, ApplicationVersionChecker};
149
use mithril_signer::{
1510
Config, ProductionServiceBuilder, ServiceBuilder, SignerRunner, SignerState, StateMachine,
1611
};
@@ -66,43 +61,6 @@ fn build_logger(min_level: Level) -> Logger {
6661
Logger::root(Arc::new(drain), o!())
6762
}
6863

69-
fn check_database(filepath: &PathBuf) -> Result<(), Box<dyn Error>> {
70-
let connection = Connection::open(filepath)?;
71-
let provider = VersionProvider::new(&connection);
72-
provider.create_table_if_not_exists()?;
73-
let application_type = ApplicationNodeType::new("aggregator")?;
74-
let maybe_option = provider.get_application_version(&application_type)?;
75-
let current_version = ApplicationVersion {
76-
semver: Version::parse(env!("CARGO_PKG_VERSION"))?,
77-
application_type,
78-
updated_at: Local::now().naive_local(),
79-
};
80-
81-
match maybe_option {
82-
None => {
83-
let provider = VersionUpdaterProvider::new(&connection);
84-
let _ = provider.save(current_version)?;
85-
debug!("application version saved in database");
86-
}
87-
Some(version) => {
88-
let req = VersionReq::parse(&current_version.semver.to_string()).unwrap();
89-
90-
if !req.matches(&version.semver) {
91-
warn!(
92-
"application version '{}' is out of date, new version is '{}'. Upgrading database…",
93-
version.semver, current_version.semver
94-
);
95-
let upgrader_provider = VersionUpdaterProvider::new(&connection);
96-
upgrader_provider.save(current_version)?;
97-
debug!("database updated");
98-
} else {
99-
debug!("database up to date");
100-
}
101-
}
102-
};
103-
Ok(())
104-
}
105-
10664
#[tokio::main]
10765
async fn main() -> Result<(), Box<dyn Error>> {
10866
// Load args
@@ -125,7 +83,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
12583
.try_deserialize()
12684
.map_err(|e| format!("configuration deserialize error: {}", e))?;
12785
let services = ProductionServiceBuilder::new(&config).build()?;
128-
check_database(&config.data_stores_directory)?;
86+
ApplicationVersionChecker::new(
87+
slog_scope::logger(),
88+
ApplicationNodeType::Signer,
89+
config.get_sqlite_file(),
90+
)
91+
.check(env!("CARGO_PKG_VERSION"))?;
12992
debug!("Started"; "run_mode" => &args.run_mode, "config" => format!("{:?}", config));
13093

13194
let mut state_machine = StateMachine::new(

0 commit comments

Comments
 (0)