mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-18 16:21:24 +01:00
add 'db migrate' command option to specify target version
This commit is contained in:
parent
6446822bf2
commit
b93d4f471c
3 changed files with 164 additions and 25 deletions
91
src/cli.rs
91
src/cli.rs
|
|
@ -3,7 +3,7 @@
|
||||||
//! CLI argument structs are defined in this module. Execution logic for each
|
//! CLI argument structs are defined in this module. Execution logic for each
|
||||||
//! command goes in a submodule.
|
//! command goes in a submodule.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
|
@ -78,6 +78,23 @@ pub(crate) struct MigrateDbArgs {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
config: ConfigArg,
|
config: ConfigArg,
|
||||||
|
|
||||||
|
/// Target server implementation to migrate database to.
|
||||||
|
///
|
||||||
|
/// If migrating to the current version of grapevine, specify the version
|
||||||
|
/// as 'grapevine'.
|
||||||
|
///
|
||||||
|
/// If migrating to a released version of conduit, specified the version
|
||||||
|
/// of conduit as `conduit-{version}` (example: `conduit-0.8.0`). If
|
||||||
|
/// migrating to an unreleased conduit build, instead specify the raw
|
||||||
|
/// database version as `conduit-db-{version}` (example: `conduit-db-13`).
|
||||||
|
/// The raw database version can be found by looking at the
|
||||||
|
/// `latest_database_version` variable in `src/database/mod.rs`.
|
||||||
|
///
|
||||||
|
/// The server implementation used for the current database will be
|
||||||
|
/// detected automatically, and does not need to be specified.
|
||||||
|
#[clap(long)]
|
||||||
|
pub(crate) to: DbMigrationTarget,
|
||||||
|
|
||||||
/// Path to read database from.
|
/// Path to read database from.
|
||||||
#[clap(long = "in", short)]
|
#[clap(long = "in", short)]
|
||||||
pub(crate) in_path: PathBuf,
|
pub(crate) in_path: PathBuf,
|
||||||
|
|
@ -87,6 +104,78 @@ pub(crate) struct MigrateDbArgs {
|
||||||
pub(crate) out_path: PathBuf,
|
pub(crate) out_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum DbMigrationTarget {
|
||||||
|
/// The latest grapevine db version
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!("grapevine".parse(), Ok(DbMigrationTarget::Grapevine))
|
||||||
|
/// ```
|
||||||
|
Grapevine,
|
||||||
|
/// A conduit-compatible db version.
|
||||||
|
///
|
||||||
|
/// This may either be specified as a released version number or directly
|
||||||
|
/// as a database version. The raw database version must be used when
|
||||||
|
/// migrating to a conduit deployment built from an unreleased commit
|
||||||
|
/// on the `next` branch.
|
||||||
|
Conduit(ConduitDbVersion),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum ConduitDbVersion {
|
||||||
|
/// A conduit release version number
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "conduit-0.8.0".parse(),
|
||||||
|
/// Ok(DbMigrationTarget::Conduit(ConduitDbVersion::Release("0.8.0")))
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
Release(String),
|
||||||
|
/// A raw database version
|
||||||
|
///
|
||||||
|
/// This corresponds directly to a
|
||||||
|
/// [`crate::service::globals::DbVersion::Conduit`] version.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!(
|
||||||
|
/// "conduit-db-13".parse(),
|
||||||
|
/// Ok(DbMigrationTarget::Conduit(ConduitDbVersion::Db(13)))
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
Db(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
#[error("invalid db migration target version")]
|
||||||
|
pub(crate) struct DbMigrationTargetParseError;
|
||||||
|
|
||||||
|
impl FromStr for DbMigrationTarget {
|
||||||
|
type Err = DbMigrationTargetParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s == "grapevine" {
|
||||||
|
Ok(DbMigrationTarget::Grapevine)
|
||||||
|
} else if let Some(version) = s.strip_prefix("conduit-db-") {
|
||||||
|
let version =
|
||||||
|
version.parse().map_err(|_| DbMigrationTargetParseError)?;
|
||||||
|
Ok(DbMigrationTarget::Conduit(ConduitDbVersion::Db(version)))
|
||||||
|
} else if let Some(version) = s.strip_prefix("conduit-") {
|
||||||
|
Ok(DbMigrationTarget::Conduit(ConduitDbVersion::Release(
|
||||||
|
version.to_owned(),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Err(DbMigrationTargetParseError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub(crate) async fn run(self) -> Result<(), error::Main> {
|
pub(crate) async fn run(self) -> Result<(), error::Main> {
|
||||||
match self.command {
|
match self.command {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,34 @@
|
||||||
use super::MigrateDbArgs;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use super::{ConduitDbVersion, DbMigrationTarget, MigrateDbArgs};
|
||||||
use crate::{
|
use crate::{
|
||||||
config, database::KeyValueDatabase, error, observability,
|
config, database::KeyValueDatabase, error, observability,
|
||||||
service::globals::DbVersion, services, utils::copy_dir, Services,
|
service::globals::DbVersion, services, utils::copy_dir, Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
impl DbMigrationTarget {
|
||||||
|
fn to_db_version(&self) -> Result<DbVersion, error::MigrateDbCommand> {
|
||||||
|
use error::MigrateDbCommand as Error;
|
||||||
|
|
||||||
|
let latest_grapevine_version = DbVersion::Grapevine(0);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
DbMigrationTarget::Grapevine => Ok(latest_grapevine_version),
|
||||||
|
DbMigrationTarget::Conduit(ConduitDbVersion::Db(version)) => {
|
||||||
|
Ok(DbVersion::Conduit(*version))
|
||||||
|
}
|
||||||
|
DbMigrationTarget::Conduit(ConduitDbVersion::Release(version)) => {
|
||||||
|
match &**version {
|
||||||
|
"0.8.0" => Ok(DbVersion::Conduit(13)),
|
||||||
|
_ => Err(Error::TargetVersionUnsupported),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn run(
|
pub(crate) async fn run(
|
||||||
args: MigrateDbArgs,
|
args: MigrateDbArgs,
|
||||||
) -> Result<(), error::MigrateDbCommand> {
|
) -> Result<(), error::MigrateDbCommand> {
|
||||||
|
|
@ -32,24 +57,53 @@ pub(crate) async fn run(
|
||||||
|
|
||||||
services().globals.err_if_server_name_changed()?;
|
services().globals.err_if_server_name_changed()?;
|
||||||
|
|
||||||
// Migrate from a grapevine-compatible db to a conduit-0.8.0-compatible db
|
let get_current =
|
||||||
|
|| services().globals.database_version().map_err(Error::MigrateDb);
|
||||||
|
let current = get_current()?;
|
||||||
|
let target = args.to.to_db_version()?;
|
||||||
|
let latest = DbVersion::Grapevine(0);
|
||||||
|
|
||||||
let version =
|
info!("Migrating from {current:?} to {target:?}");
|
||||||
services().globals.database_version().map_err(Error::MigrateDb)?;
|
|
||||||
if version < DbVersion::Grapevine(0) {
|
if target == current {
|
||||||
return Err(Error::DbVersionTooOld(version));
|
// No-op
|
||||||
} else if version != DbVersion::Grapevine(0) {
|
} else if target == latest {
|
||||||
return Err(Error::DbVersionUnsupported(version));
|
// Migrate to latest grapevine
|
||||||
};
|
if !current.partial_cmp(&latest).is_some_and(Ordering::is_le) {
|
||||||
|
return Err(Error::DbVersionUnsupported(current));
|
||||||
|
}
|
||||||
|
db.apply_migrations().await.map_err(Error::MigrateDb)?;
|
||||||
|
} else if target == DbVersion::Conduit(13) {
|
||||||
|
// Migrate to latest grapevine so we have a consistent starting point
|
||||||
|
if !current.partial_cmp(&latest).is_some_and(Ordering::is_le) {
|
||||||
|
return Err(Error::DbVersionUnsupported(current));
|
||||||
|
}
|
||||||
|
db.apply_migrations().await.map_err(Error::MigrateDb)?;
|
||||||
|
assert_eq!(
|
||||||
|
get_current()?,
|
||||||
|
latest,
|
||||||
|
"should have migrated to latest version"
|
||||||
|
);
|
||||||
|
|
||||||
// Undo Conduit(13) -> Grapevine(0)
|
// Undo Conduit(13) -> Grapevine(0)
|
||||||
//
|
//
|
||||||
// This is a no-op that only changes the db version namespace. Setting the
|
// This is a no-op that only changes the db version namespace. Setting
|
||||||
// version to Conduit(_) will restore the original state.
|
// the version to Conduit(_) will restore the original state.
|
||||||
services()
|
services()
|
||||||
.globals
|
.globals
|
||||||
.bump_database_version(DbVersion::Conduit(13))
|
.bump_database_version(DbVersion::Conduit(13))
|
||||||
.map_err(Error::MigrateDb)?;
|
.map_err(Error::MigrateDb)?;
|
||||||
|
} else {
|
||||||
|
return Err(Error::TargetVersionUnsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_current()?,
|
||||||
|
target,
|
||||||
|
"should have migrated to target version"
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("Migration successful");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/error.rs
10
src/error.rs
|
|
@ -118,15 +118,11 @@ pub(crate) enum MigrateDbCommand {
|
||||||
#[error("failed to migrate database")]
|
#[error("failed to migrate database")]
|
||||||
MigrateDb(#[source] crate::utils::error::Error),
|
MigrateDb(#[source] crate::utils::error::Error),
|
||||||
|
|
||||||
#[error(
|
|
||||||
"initial database version is too old for migration: {_0:?}. Try \
|
|
||||||
loading the database with the latest conduit release and then \
|
|
||||||
attempting migration again."
|
|
||||||
)]
|
|
||||||
DbVersionTooOld(DbVersion),
|
|
||||||
|
|
||||||
#[error("initial database version is not supported for migration: {_0:?}")]
|
#[error("initial database version is not supported for migration: {_0:?}")]
|
||||||
DbVersionUnsupported(DbVersion),
|
DbVersionUnsupported(DbVersion),
|
||||||
|
|
||||||
|
#[error("target database version is not supported for migration")]
|
||||||
|
TargetVersionUnsupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors copying a directory recursively.
|
/// Errors copying a directory recursively.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue