mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-31 09:18:29 +00:00
bootstrap: add --single-node flag that creates a single-node layout
This commit is contained in:
@@ -8,7 +8,7 @@ use crate::cli::local::convert_db;
|
|||||||
pub enum Command {
|
pub enum Command {
|
||||||
/// Run Garage server
|
/// Run Garage server
|
||||||
#[structopt(name = "server", version = garage_version())]
|
#[structopt(name = "server", version = garage_version())]
|
||||||
Server,
|
Server(ServerOpt),
|
||||||
|
|
||||||
/// Get network status
|
/// Get network status
|
||||||
#[structopt(name = "status", version = garage_version())]
|
#[structopt(name = "status", version = garage_version())]
|
||||||
@@ -79,6 +79,17 @@ pub enum Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// ---- garage server ... ----
|
||||||
|
// ---------------------------
|
||||||
|
|
||||||
|
#[derive(StructOpt, Debug)]
|
||||||
|
pub struct ServerOpt {
|
||||||
|
/// Automatically configure a single-node layout in the cluster
|
||||||
|
#[structopt(long = "single-node")]
|
||||||
|
pub(crate) single_node: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// ---- garage node ... ----
|
// ---- garage node ... ----
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|||||||
+2
-2
@@ -158,7 +158,7 @@ fn main() {
|
|||||||
|
|
||||||
async fn run(opt: Opt) -> Result<(), Error> {
|
async fn run(opt: Opt) -> Result<(), Error> {
|
||||||
match opt.cmd {
|
match opt.cmd {
|
||||||
Command::Server => server::run_server(opt.config_file, opt.secrets).await,
|
Command::Server(sopt) => server::run_server(opt.config_file, opt.secrets, sopt).await,
|
||||||
Command::OfflineRepair(repair_opt) => {
|
Command::OfflineRepair(repair_opt) => {
|
||||||
cli::local::repair::offline_repair(opt.config_file, opt.secrets, repair_opt).await
|
cli::local::repair::offline_repair(opt.config_file, opt.secrets, repair_opt).await
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ async fn run(opt: Opt) -> Result<(), Error> {
|
|||||||
fn init_logging(opt: &Opt) {
|
fn init_logging(opt: &Opt) {
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
let default_log = match &opt.cmd {
|
let default_log = match &opt.cmd {
|
||||||
Command::Server => "netapp=info,garage=info",
|
Command::Server(_) => "netapp=info,garage=info",
|
||||||
_ => "netapp=warn,garage=warn",
|
_ => "netapp=warn,garage=warn",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+69
-1
@@ -1,4 +1,5 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ use garage_api_k2v::api_server::K2VApiServer;
|
|||||||
|
|
||||||
use crate::secrets::{fill_secrets, Secrets};
|
use crate::secrets::{fill_secrets, Secrets};
|
||||||
use crate::tracing_setup::init_tracing;
|
use crate::tracing_setup::init_tracing;
|
||||||
|
use crate::ServerOpt;
|
||||||
|
|
||||||
async fn wait_from(mut chan: watch::Receiver<bool>) {
|
async fn wait_from(mut chan: watch::Receiver<bool>) {
|
||||||
while !*chan.borrow() {
|
while !*chan.borrow() {
|
||||||
@@ -25,7 +27,11 @@ async fn wait_from(mut chan: watch::Receiver<bool>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Error> {
|
pub async fn run_server(
|
||||||
|
config_file: PathBuf,
|
||||||
|
secrets: Secrets,
|
||||||
|
opt: ServerOpt,
|
||||||
|
) -> Result<(), Error> {
|
||||||
info!("Loading configuration...");
|
info!("Loading configuration...");
|
||||||
let config = fill_secrets(read_config(config_file)?, secrets)?;
|
let config = fill_secrets(read_config(config_file)?, secrets)?;
|
||||||
|
|
||||||
@@ -67,6 +73,10 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
|||||||
info!("Launching internal Garage cluster communications...");
|
info!("Launching internal Garage cluster communications...");
|
||||||
let run_system = tokio::spawn(garage.system.clone().run(watch_cancel.clone()));
|
let run_system = tokio::spawn(garage.system.clone().run(watch_cancel.clone()));
|
||||||
|
|
||||||
|
// ---- Run initial configuration logic ----
|
||||||
|
|
||||||
|
initial_config(&garage, opt).await?;
|
||||||
|
|
||||||
// ---- Launch public-facing API servers ----
|
// ---- Launch public-facing API servers ----
|
||||||
|
|
||||||
let mut servers = vec![];
|
let mut servers = vec![];
|
||||||
@@ -164,6 +174,64 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn initial_config(garage: &Arc<Garage>, opt: ServerOpt) -> Result<(), Error> {
|
||||||
|
use garage_rpc::layout::*;
|
||||||
|
|
||||||
|
if opt.single_node {
|
||||||
|
let layout_version = garage.system.cluster_layout().inner().current().version;
|
||||||
|
|
||||||
|
if layout_version > 1 {
|
||||||
|
return Err(Error::Message("Refusing to run in single-node mode: layout version is already superior to 1. Remove the --single-node flag to run the server in full mode.".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if layout_version == 0 {
|
||||||
|
// Setup initial layout
|
||||||
|
let mut layout = garage.system.cluster_layout().inner().clone();
|
||||||
|
let our_id = garage.system.id;
|
||||||
|
|
||||||
|
// Check no other nodes are present in the system
|
||||||
|
let nodes = garage.system.get_known_nodes();
|
||||||
|
if nodes.iter().any(|x| x.id != our_id) {
|
||||||
|
return Err(Error::Message("Refusing to run in single-node mode: more nodes are already present in the cluster.".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically determine this node's capacity
|
||||||
|
let capacity = garage
|
||||||
|
.system
|
||||||
|
.local_status()
|
||||||
|
.data_disk_avail
|
||||||
|
.map(|(_avail, total)| total)
|
||||||
|
.unwrap_or(1024 * 1024 * 1024); // Default to 1GB
|
||||||
|
|
||||||
|
assert!(layout.current().roles.is_empty());
|
||||||
|
|
||||||
|
layout.staging.get_mut().roles.clear();
|
||||||
|
layout.staging.get_mut().roles.update_in_place(
|
||||||
|
our_id,
|
||||||
|
NodeRoleV(Some(NodeRole {
|
||||||
|
zone: "dc1".to_string(),
|
||||||
|
capacity: Some(capacity),
|
||||||
|
tags: vec!["default".to_string()],
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
let (layout, msg) = layout.apply_staged_changes(1)?;
|
||||||
|
info!(
|
||||||
|
"Created initial layout for single-node configuration:\n{}",
|
||||||
|
msg.join("\n")
|
||||||
|
);
|
||||||
|
|
||||||
|
garage
|
||||||
|
.system
|
||||||
|
.layout_manager
|
||||||
|
.update_cluster_layout(&layout)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn watch_shutdown_signal() -> watch::Receiver<bool> {
|
fn watch_shutdown_signal() -> watch::Receiver<bool> {
|
||||||
use tokio::signal::unix::*;
|
use tokio::signal::unix::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user