mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-27 15:37:03 +00:00
bootstrap: add --default-access-key and --default-bucket flags
This commit is contained in:
@@ -85,9 +85,18 @@ pub enum Command {
|
|||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
pub struct ServerOpt {
|
pub struct ServerOpt {
|
||||||
/// Automatically configure a single-node layout in the cluster
|
/// Automatically configure a single-node layout in the cluster.
|
||||||
|
/// Garage will refuse to run if the cluster already has other nodes.
|
||||||
#[structopt(long = "single-node")]
|
#[structopt(long = "single-node")]
|
||||||
pub(crate) single_node: bool,
|
pub(crate) single_node: bool,
|
||||||
|
/// Configure a default S3 API key using environment variables `GARAGE_DEFAULT_ACCESS_KEY` and
|
||||||
|
/// `GARAGE_DEFAULT_SECRET_KEY`. Requires `--single-node`.
|
||||||
|
#[structopt(long = "default-access-key")]
|
||||||
|
pub(crate) default_access_key: bool,
|
||||||
|
/// Configure a default bucket using environment variable `GARAGE_DEFAULT_BUCKET`.
|
||||||
|
/// Implies `--default-access-key`. Requires `--single-node`.
|
||||||
|
#[structopt(long = "default-bucket")]
|
||||||
|
pub(crate) default_bucket: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|||||||
+115
-4
@@ -50,6 +50,9 @@ pub async fn run_server(
|
|||||||
info!("Initializing Garage main data store...");
|
info!("Initializing Garage main data store...");
|
||||||
let garage = Garage::new(config.clone())?;
|
let garage = Garage::new(config.clone())?;
|
||||||
|
|
||||||
|
// Handle --single-node, --default-bucket and --default-access-key
|
||||||
|
initial_config(&garage, opt).await?;
|
||||||
|
|
||||||
info!("Initializing background runner...");
|
info!("Initializing background runner...");
|
||||||
let watch_cancel = watch_shutdown_signal();
|
let watch_cancel = watch_shutdown_signal();
|
||||||
let (background, await_background_done) = BackgroundRunner::new(watch_cancel.clone());
|
let (background, await_background_done) = BackgroundRunner::new(watch_cancel.clone());
|
||||||
@@ -73,10 +76,6 @@ pub async fn run_server(
|
|||||||
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![];
|
||||||
@@ -175,9 +174,23 @@ pub async fn run_server(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn initial_config(garage: &Arc<Garage>, opt: ServerOpt) -> Result<(), Error> {
|
async fn initial_config(garage: &Arc<Garage>, opt: ServerOpt) -> Result<(), Error> {
|
||||||
|
use garage_model::bucket_alias_table::is_valid_bucket_name;
|
||||||
|
use garage_model::bucket_table::Bucket;
|
||||||
|
use garage_model::key_table::*;
|
||||||
|
use garage_model::permission::BucketKeyPerm;
|
||||||
use garage_rpc::layout::*;
|
use garage_rpc::layout::*;
|
||||||
|
use garage_rpc::replication_mode::ReplicationFactor;
|
||||||
|
use garage_table::*;
|
||||||
|
use garage_util::time::now_msec;
|
||||||
|
|
||||||
if opt.single_node {
|
if opt.single_node {
|
||||||
|
if garage.replication_factor != ReplicationFactor::new(1).unwrap() {
|
||||||
|
return Err(Error::Message(
|
||||||
|
"Single-node mode requires replication_factor = 1 in the configuration file."
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let layout_version = garage.system.cluster_layout().inner().current().version;
|
let layout_version = garage.system.cluster_layout().inner().current().version;
|
||||||
|
|
||||||
if layout_version > 1 {
|
if layout_version > 1 {
|
||||||
@@ -229,6 +242,104 @@ async fn initial_config(garage: &Arc<Garage>, opt: ServerOpt) -> Result<(), Erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opt.default_bucket || opt.default_access_key) && !opt.single_node {
|
||||||
|
return Err(Error::Message(
|
||||||
|
"Flags --default-access-key and --default-bucket can only be used in single-node mode."
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.default_access_key || opt.default_bucket {
|
||||||
|
let rdenv = |name: &str| {
|
||||||
|
std::env::var(name)
|
||||||
|
.map_err(|_| Error::Message(format!("Environment variable `{}` is not set", name)))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create default access key if it does not exist
|
||||||
|
let key_id = rdenv("GARAGE_DEFAULT_ACCESS_KEY")?;
|
||||||
|
let secret_key = rdenv("GARAGE_DEFAULT_SECRET_KEY")?;
|
||||||
|
|
||||||
|
let existing_key = garage.key_table.get(&EmptyKey, &key_id).await?;
|
||||||
|
|
||||||
|
let key = match existing_key {
|
||||||
|
Some(key) => {
|
||||||
|
match key.state.as_option() {
|
||||||
|
None => return Err(Error::Message(format!("Access key {} was deleted in the cluster, cannot add it back", key_id))),
|
||||||
|
Some(st) if st.secret_key != secret_key => return Err(Error::Message(format!("Access key {} is associated with a secret key different than the one given in GARAGE_DEFAULT_SECRET_KEY", key_id))),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
key
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Creating default access key `{}`", key_id);
|
||||||
|
|
||||||
|
let mut key = Key::import(&key_id, &secret_key, "default access key")
|
||||||
|
.map_err(|e| Error::Message(format!("Invalid default access key: {}", e)))?;
|
||||||
|
key.state
|
||||||
|
.as_option_mut()
|
||||||
|
.unwrap()
|
||||||
|
.allow_create_bucket
|
||||||
|
.update(true);
|
||||||
|
garage.key_table.insert(&key).await?;
|
||||||
|
|
||||||
|
key
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if opt.default_bucket {
|
||||||
|
// Create default bucket if it does not exist
|
||||||
|
let bucket_name = rdenv("GARAGE_DEFAULT_BUCKET")?;
|
||||||
|
|
||||||
|
if !is_valid_bucket_name(&bucket_name, garage.config.allow_punycode) {
|
||||||
|
return Err(Error::Message(
|
||||||
|
"Invalid default bucket name, see S3 specification for allowed bucket names."
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let helper = garage.locked_helper().await;
|
||||||
|
|
||||||
|
let bucket = match helper.bucket().resolve_global_bucket_fast(&bucket_name)? {
|
||||||
|
Some(bucket) => bucket,
|
||||||
|
None => {
|
||||||
|
info!("Creating default bucket `{}`", bucket_name);
|
||||||
|
|
||||||
|
let bucket = Bucket::new();
|
||||||
|
garage.bucket_table.insert(&bucket).await?;
|
||||||
|
|
||||||
|
helper
|
||||||
|
.set_global_bucket_alias(bucket.id, &bucket_name)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::Message(format!("Cannot create default bucket: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
bucket
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
helper
|
||||||
|
.set_bucket_key_permissions(
|
||||||
|
bucket.id,
|
||||||
|
&key.key_id,
|
||||||
|
BucketKeyPerm {
|
||||||
|
timestamp: now_msec(),
|
||||||
|
allow_read: true,
|
||||||
|
allow_write: true,
|
||||||
|
allow_owner: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::Message(format!(
|
||||||
|
"Cannot configure permissions on default bucket: {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user