mirror of
https://git.deuxfleurs.fr/Deuxfleurs/garage.git
synced 2026-05-14 21:26:53 -04:00
Compare commits
1 commit
main-v1
...
dump-table
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bccddb504 |
5 changed files with 119 additions and 2 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1321,6 +1321,7 @@ dependencies = [
|
||||||
"opentelemetry-prometheus",
|
"opentelemetry-prometheus",
|
||||||
"parse_duration",
|
"parse_duration",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ structopt.workspace = true
|
||||||
git-version.workspace = true
|
git-version.workspace = true
|
||||||
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
serde_bytes.workspace = true
|
||||||
|
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,10 @@ pub enum NodeOperation {
|
||||||
/// Connect to Garage node that is currently isolated from the system
|
/// Connect to Garage node that is currently isolated from the system
|
||||||
#[structopt(name = "connect", version = garage_version())]
|
#[structopt(name = "connect", version = garage_version())]
|
||||||
Connect(ConnectNodeOpt),
|
Connect(ConnectNodeOpt),
|
||||||
|
|
||||||
|
/// Dump the content of a metadata table as JSON lines
|
||||||
|
#[structopt(name = "dump", version = garage_version())]
|
||||||
|
Dump(DumpNodeOpt),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
|
|
@ -88,6 +92,12 @@ pub struct ConnectNodeOpt {
|
||||||
pub(crate) node: String,
|
pub(crate) node: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(StructOpt, Debug)]
|
||||||
|
pub struct DumpNodeOpt {
|
||||||
|
/// Name of the data table to dump
|
||||||
|
pub(crate) what: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
pub enum LayoutOperation {
|
pub enum LayoutOperation {
|
||||||
/// Assign role to Garage node
|
/// Assign role to Garage node
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,14 @@ async fn main() {
|
||||||
let res = match opt.cmd {
|
let res = match opt.cmd {
|
||||||
Command::Server => server::run_server(opt.config_file, opt.secrets).await,
|
Command::Server => server::run_server(opt.config_file, opt.secrets).await,
|
||||||
Command::OfflineRepair(repair_opt) => {
|
Command::OfflineRepair(repair_opt) => {
|
||||||
repair::offline::offline_repair(opt.config_file, opt.secrets, repair_opt).await
|
repair::offline::offline_repair(opt.config_file, opt.secrets, repair_opt)
|
||||||
}
|
}
|
||||||
Command::ConvertDb(conv_opt) => {
|
Command::ConvertDb(conv_opt) => {
|
||||||
cli::convert_db::do_conversion(conv_opt).map_err(From::from)
|
cli::convert_db::do_conversion(conv_opt).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
Command::Node(NodeOperation::Dump(dump_opt)) => {
|
||||||
|
repair::offline::dump(opt.config_file, opt.secrets, dump_opt)
|
||||||
|
}
|
||||||
Command::Node(NodeOperation::NodeId(node_id_opt)) => {
|
Command::Node(NodeOperation::NodeId(node_id_opt)) => {
|
||||||
node_id_command(opt.config_file, node_id_opt.quiet)
|
node_id_command(opt.config_file, node_id_opt.quiet)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use garage_util::config::*;
|
use garage_util::config::*;
|
||||||
use garage_util::error::*;
|
use garage_util::error::*;
|
||||||
|
|
||||||
use garage_model::garage::Garage;
|
use garage_model::garage::Garage;
|
||||||
|
use garage_table::{replication::TableReplication, *};
|
||||||
|
|
||||||
use crate::cli::structs::*;
|
use crate::cli::structs::*;
|
||||||
use crate::secrets::{fill_secrets, Secrets};
|
use crate::secrets::{fill_secrets, Secrets};
|
||||||
|
|
||||||
pub async fn offline_repair(
|
pub fn offline_repair(
|
||||||
config_file: PathBuf,
|
config_file: PathBuf,
|
||||||
secrets: Secrets,
|
secrets: Secrets,
|
||||||
opt: OfflineRepairOpt,
|
opt: OfflineRepairOpt,
|
||||||
|
|
@ -45,3 +49,100 @@ pub async fn offline_repair(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dump(config_file: PathBuf, secrets: Secrets, opt: DumpNodeOpt) -> Result<(), Error> {
|
||||||
|
let what = opt.what.as_str();
|
||||||
|
|
||||||
|
info!("Loading configuration...");
|
||||||
|
let config = fill_secrets(read_config(config_file)?, secrets)?;
|
||||||
|
|
||||||
|
info!("Initializing Garage main data store...");
|
||||||
|
let garage = Garage::new(config)?;
|
||||||
|
|
||||||
|
match what {
|
||||||
|
"bucket" | "buckets" => dump_table_inner(&garage.bucket_table),
|
||||||
|
"bucket_alias" | "bucket_aliases" => dump_table_inner(&garage.bucket_alias_table),
|
||||||
|
"key" | "keys" => dump_table_inner(&garage.key_table),
|
||||||
|
"object" | "objects" => dump_table_inner(&garage.object_table),
|
||||||
|
"object_counter" | "object_counters" => Err(Error::Message(
|
||||||
|
"object_counters cannot be JSON-serialized".into(),
|
||||||
|
)),
|
||||||
|
"mpu" => dump_table_inner(&garage.mpu_table),
|
||||||
|
"mpu_counter" | "mpu_counters" => Err(Error::Message(
|
||||||
|
"mpu_counters cannot be JSON-serialized".into(),
|
||||||
|
)),
|
||||||
|
"version" | "versions" => dump_table_inner(&garage.version_table),
|
||||||
|
"block_ref" | "block_refs" => dump_table_inner(&garage.block_ref_table),
|
||||||
|
#[cfg(feature = "k2v")]
|
||||||
|
"k2v_item" | "k2v_items" => dump_table_inner(&garage.k2v.item_table),
|
||||||
|
//#[cfg(feature = "k2v")]
|
||||||
|
"k2v_counter" | "k2v_counters" => Err(Error::Message(
|
||||||
|
"k2v_counters cannot be JSON-serialized".into(),
|
||||||
|
)),
|
||||||
|
other => {
|
||||||
|
let mut stdout = std::io::stdout().lock();
|
||||||
|
match other {
|
||||||
|
"cluster_layout" => Err(Error::Message(
|
||||||
|
"cluster_layout cannot be JSON-serialized".into(),
|
||||||
|
)),
|
||||||
|
_ => Err(Error::Message(format!("invalid thing to dump: {}", what))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct DumpEntry<'a, T: Serialize> {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
partition_key: &'a [u8],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
sort_key: &'a [u8],
|
||||||
|
entry: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_table_inner<F, R>(table: &Table<F, R>) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
F: TableSchema,
|
||||||
|
R: TableReplication,
|
||||||
|
{
|
||||||
|
eprintln!("Dumping table {}...", F::TABLE_NAME);
|
||||||
|
|
||||||
|
let mut stdout = std::io::stdout().lock();
|
||||||
|
|
||||||
|
for line in table.data.store.iter()? {
|
||||||
|
let (_k, v) = line?;
|
||||||
|
let v_dec = table.data.decode_entry(&v)?;
|
||||||
|
let pkh = v_dec.partition_key().hash();
|
||||||
|
let dump_entry = DumpEntry {
|
||||||
|
partition_key: pkh.as_slice(),
|
||||||
|
sort_key: v_dec.sort_key().sort_key(),
|
||||||
|
entry: &v_dec,
|
||||||
|
};
|
||||||
|
dump_line(&mut stdout, dump_entry)?;
|
||||||
|
}
|
||||||
|
stdout.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dump_line<T: Serialize>(
|
||||||
|
mut stdout: &mut std::io::StdoutLock<'static>,
|
||||||
|
dump_entry: T,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut ser = serde_json::ser::Serializer::with_formatter(&mut stdout, DumpFormatter);
|
||||||
|
dump_entry.serialize(&mut ser)?;
|
||||||
|
stdout.write_all(b"\n")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DumpFormatter;
|
||||||
|
impl serde_json::ser::Formatter for DumpFormatter {
|
||||||
|
fn write_byte_array<W>(&mut self, writer: &mut W, value: &[u8]) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
W: ?Sized + std::io::Write,
|
||||||
|
{
|
||||||
|
writer.write_all(b"\"")?;
|
||||||
|
writer.write_all(hex::encode(&value).as_bytes())?;
|
||||||
|
writer.write_all(b"\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue