forked from mirror/grapevine
WIP: migrate-db command
This commit is contained in:
parent
e7746386e4
commit
d8bad2da09
3 changed files with 101 additions and 6 deletions
43
src/cli.rs
43
src/cli.rs
|
@ -5,10 +5,11 @@
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand, ValueEnum};
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
|
||||||
|
mod migrate_db;
|
||||||
mod serve;
|
mod serve;
|
||||||
|
|
||||||
/// Command line arguments
|
/// Command line arguments
|
||||||
|
@ -25,6 +26,15 @@ pub(crate) struct Args {
|
||||||
pub(crate) command: Option<Command>,
|
pub(crate) command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub(crate) enum Command {
|
||||||
|
/// Run the server. Default if no command is specified.
|
||||||
|
Serve,
|
||||||
|
|
||||||
|
/// Migrate database to be used by a different server implementation.
|
||||||
|
MigrateDb(MigrateDbArgs),
|
||||||
|
}
|
||||||
|
|
||||||
/// CLI args shared between all commands.
|
/// CLI args shared between all commands.
|
||||||
#[derive(clap::Args)]
|
#[derive(clap::Args)]
|
||||||
#[clap(mut_arg("config", |x| {
|
#[clap(mut_arg("config", |x| {
|
||||||
|
@ -41,14 +51,32 @@ pub(crate) struct Args {
|
||||||
}))]
|
}))]
|
||||||
pub(crate) struct GlobalArgs {
|
pub(crate) struct GlobalArgs {
|
||||||
/// Path to the configuration file
|
/// Path to the configuration file
|
||||||
#[clap(long, short)]
|
#[clap(long, short, global = true)]
|
||||||
pub(crate) config: Option<PathBuf>,
|
pub(crate) config: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Clone)]
|
// TODO: Name in a way that allows versions to be distinguished
|
||||||
pub(crate) enum Command {
|
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
/// Run the server. Default if no command is specified.
|
pub(crate) enum DbMigrationTarget {
|
||||||
Serve,
|
Grapevine,
|
||||||
|
Conduit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(clap::Args)]
|
||||||
|
pub(crate) struct MigrateDbArgs {
|
||||||
|
#[clap(long)]
|
||||||
|
pub(crate) from: DbMigrationTarget,
|
||||||
|
|
||||||
|
#[clap(long)]
|
||||||
|
pub(crate) to: DbMigrationTarget,
|
||||||
|
|
||||||
|
/// Path to read database from.
|
||||||
|
#[clap(long = "in", short)]
|
||||||
|
pub(crate) in_path: PathBuf,
|
||||||
|
|
||||||
|
/// Path to write migrated database to.
|
||||||
|
#[clap(long = "out", short)]
|
||||||
|
pub(crate) out_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
|
@ -60,6 +88,9 @@ impl Args {
|
||||||
|
|
||||||
match command.unwrap_or(Command::Serve) {
|
match command.unwrap_or(Command::Serve) {
|
||||||
Command::Serve => serve::run(global_args).await,
|
Command::Serve => serve::run(global_args).await,
|
||||||
|
Command::MigrateDb(args) => {
|
||||||
|
Ok(migrate_db::run(global_args, args).await?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
src/cli/migrate_db.rs
Normal file
39
src/cli/migrate_db.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use super::{GlobalArgs, MigrateDbArgs};
|
||||||
|
use crate::{
|
||||||
|
services,
|
||||||
|
config, database::{KeyValueDatabase, DbVersion}, error, observability, utils::copy_dir,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) async fn run(
|
||||||
|
global_args: GlobalArgs,
|
||||||
|
args: MigrateDbArgs,
|
||||||
|
) -> Result<(), error::MigrateDbCommand> {
|
||||||
|
use error::MigrateDbCommand as Error;
|
||||||
|
|
||||||
|
let mut config = config::load(global_args.config.as_ref()).await?;
|
||||||
|
// mutating the config like this is ugly, but difficult to avoid. Currently
|
||||||
|
// the database is very tightly coupled with service code, which reads the
|
||||||
|
// path only from the config.
|
||||||
|
config.database.path =
|
||||||
|
args.out_path.to_str().ok_or(Error::InvalidUnicodeOutPath)?.to_owned();
|
||||||
|
|
||||||
|
let (_guard, reload_handles) = observability::init(&config)?;
|
||||||
|
|
||||||
|
copy_dir(&args.in_path, &args.out_path).await.map_err(Error::Copy)?;
|
||||||
|
|
||||||
|
let db = KeyValueDatabase::load_or_create(config, reload_handles)
|
||||||
|
.map_err(Error::LoadDatabase)?;
|
||||||
|
|
||||||
|
let version = services().globals.database_version().await?;
|
||||||
|
if version != DbVersion::Grapevine(0) {
|
||||||
|
return Err(Error::WrongDbVersion(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo Conduit(13) -> Grapevine(0)
|
||||||
|
//
|
||||||
|
// This is a no-op that only changes the db version namespace. Setting the
|
||||||
|
// version to Conduit(_) will restore the original state.
|
||||||
|
services().globals.bump_database_version(DbVersion::Conduit(13)).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
25
src/error.rs
25
src/error.rs
|
@ -51,6 +51,31 @@ pub(crate) enum Main {
|
||||||
|
|
||||||
#[error("failed to serve requests")]
|
#[error("failed to serve requests")]
|
||||||
Serve(#[from] Serve),
|
Serve(#[from] Serve),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
MigrateDbCommand(#[from] MigrateDbCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Top-level errors from the `migrate-db` subcommand.
|
||||||
|
// Missing docs are allowed here since that kind of information should be
|
||||||
|
// encoded in the error messages themselves anyway.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub(crate) enum MigrateDbCommand {
|
||||||
|
#[error("output path is not valid unicode")]
|
||||||
|
InvalidUnicodeOutPath,
|
||||||
|
|
||||||
|
#[error("failed to copy existing database directory")]
|
||||||
|
Copy(#[source] CopyDir),
|
||||||
|
|
||||||
|
#[error("failed to initialize observability")]
|
||||||
|
Observability(#[from] Observability),
|
||||||
|
|
||||||
|
#[error("failed to load configuration")]
|
||||||
|
Config(#[from] Config),
|
||||||
|
|
||||||
|
#[error("failed to load database")]
|
||||||
|
LoadDatabase(#[source] crate::utils::error::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors copying a directory recursively.
|
/// Errors copying a directory recursively.
|
||||||
|
|
Loading…
Reference in a new issue