aboutsummaryrefslogtreecommitdiff
path: root/src/garage
diff options
context:
space:
mode:
Diffstat (limited to 'src/garage')
-rw-r--r--src/garage/Cargo.toml1
-rw-r--r--src/garage/admin.rs155
-rw-r--r--src/garage/cli/cmd.rs2
-rw-r--r--src/garage/cli/util.rs2
4 files changed, 105 insertions, 55 deletions
diff --git a/src/garage/Cargo.toml b/src/garage/Cargo.toml
index 44cacde3..cd6564ce 100644
--- a/src/garage/Cargo.toml
+++ b/src/garage/Cargo.toml
@@ -35,6 +35,7 @@ sled = "0.34"
rmp-serde = "0.15"
serde = { version = "1.0", default-features = false, features = ["derive", "rc"] }
+serde_bytes = "0.11"
structopt = { version = "0.3", default-features = false }
toml = "0.5"
diff --git a/src/garage/admin.rs b/src/garage/admin.rs
index 2eb0f187..9ea5c19e 100644
--- a/src/garage/admin.rs
+++ b/src/garage/admin.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
+use serde_bytes::ByteBuf;
use garage_util::crdt::*;
use garage_util::data::*;
@@ -27,6 +28,8 @@ use crate::repair::Repair;
pub const ADMIN_RPC_PATH: &str = "garage/admin_rpc.rs/Rpc";
+macro_rules! INVALID_BUCKET_NAME_MESSAGE { () => { "Invalid bucket name: {}. See AWS documentation for constraints on S3 bucket names:\nhttps://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html" }; }
+
#[derive(Debug, Serialize, Deserialize)]
pub enum AdminRpc {
BucketOperation(BucketOperation),
@@ -142,14 +145,14 @@ impl AdminRpcHandler {
}));
alias
}
- None => BucketAlias::new(name.clone(), bucket.id),
+ None => BucketAlias::new(name.clone(), bucket.id)
+ .ok_or_message(format!(INVALID_BUCKET_NAME_MESSAGE!(), name))?,
};
- bucket
- .state
- .as_option_mut()
- .unwrap()
- .aliases
- .update_in_place(name.clone(), true);
+ bucket.state.as_option_mut().unwrap().aliases.merge_raw(
+ name,
+ alias.state.timestamp(),
+ &true,
+ );
self.garage.bucket_table.insert(&bucket).await?;
self.garage.bucket_alias_table.insert(&alias).await?;
Ok(AdminRpc::Ok(format!("Bucket {} was created.", name)))
@@ -222,7 +225,7 @@ impl AdminRpcHandler {
// 2. delete bucket alias
bucket_alias.state.update(Deletable::Deleted);
self.garage.bucket_alias_table.insert(&bucket_alias).await?;
- // 3. delete bucket alias
+ // 3. delete bucket
bucket.state = Deletable::delete();
self.garage.bucket_table.insert(&bucket).await?;
@@ -259,15 +262,36 @@ impl AdminRpcHandler {
}
}
- key_param.local_aliases = key_param
- .local_aliases
- .update_mutator(query.new_name.clone(), Deletable::present(bucket_id));
- self.garage.key_table.insert(&key).await?;
+ if !is_valid_bucket_name(&query.new_name) {
+ return Err(Error::Message(format!(
+ INVALID_BUCKET_NAME_MESSAGE!(),
+ query.new_name
+ )));
+ }
+ // Checks ok, add alias
let mut bucket_p = bucket.state.as_option_mut().unwrap();
- bucket_p.local_aliases = bucket_p
- .local_aliases
- .update_mutator((key.key_id.clone(), query.new_name.clone()), true);
+ let bucket_p_local_alias_key = (key.key_id.clone(), query.new_name.clone());
+
+ // Calculate the timestamp to assign to this aliasing in the two local_aliases maps
+ // (the one from key to bucket, and the reverse one stored in the bucket iself)
+ // so that merges on both maps in case of a concurrent operation resolve
+ // to the same alias being set
+ let alias_ts = increment_logical_clock_2(
+ key_param.local_aliases.get_timestamp(&query.new_name),
+ bucket_p
+ .local_aliases
+ .get_timestamp(&bucket_p_local_alias_key),
+ );
+
+ key_param.local_aliases = LwwMap::raw_item(
+ query.new_name.clone(),
+ alias_ts,
+ Deletable::present(bucket_id),
+ );
+ self.garage.key_table.insert(&key).await?;
+
+ bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, true);
self.garage.bucket_table.insert(&bucket).await?;
Ok(AdminRpc::Ok(format!(
@@ -275,40 +299,47 @@ impl AdminRpcHandler {
query.new_name, bucket_id, key.key_id
)))
} else {
- let mut alias = self
+ let alias = self
.garage
.bucket_alias_table
.get(&EmptyKey, &query.new_name)
- .await?
- .unwrap_or(BucketAlias {
- name: query.new_name.clone(),
- state: Lww::new(Deletable::delete()),
- });
+ .await?;
- if let Some(existing_alias) = alias.state.get().as_option() {
- if existing_alias.bucket_id == bucket_id {
- return Ok(AdminRpc::Ok(format!(
- "Alias {} already points to bucket {:?}",
- query.new_name, bucket_id
- )));
- } else {
- return Err(Error::Message(format!(
- "Alias {} already exists and points to different bucket: {:?}",
- query.new_name, existing_alias.bucket_id
- )));
+ if let Some(existing_alias) = alias.as_ref() {
+ if let Some(p) = existing_alias.state.get().as_option() {
+ if p.bucket_id == bucket_id {
+ return Ok(AdminRpc::Ok(format!(
+ "Alias {} already points to bucket {:?}",
+ query.new_name, bucket_id
+ )));
+ } else {
+ return Err(Error::Message(format!(
+ "Alias {} already exists and points to different bucket: {:?}",
+ query.new_name, p.bucket_id
+ )));
+ }
}
}
// Checks ok, add alias
- alias
- .state
- .update(Deletable::present(AliasParams { bucket_id }));
+ let mut bucket_p = bucket.state.as_option_mut().unwrap();
+
+ let alias_ts = increment_logical_clock_2(
+ bucket_p.aliases.get_timestamp(&query.new_name),
+ alias.as_ref().map(|a| a.state.timestamp()).unwrap_or(0),
+ );
+
+ let alias = match alias {
+ None => BucketAlias::new(query.new_name.clone(), bucket_id)
+ .ok_or_message(format!(INVALID_BUCKET_NAME_MESSAGE!(), query.new_name))?,
+ Some(mut a) => {
+ a.state = Lww::raw(alias_ts, Deletable::present(AliasParams { bucket_id }));
+ a
+ }
+ };
self.garage.bucket_alias_table.insert(&alias).await?;
- let mut bucket_p = bucket.state.as_option_mut().unwrap();
- bucket_p.aliases = bucket_p
- .aliases
- .update_mutator(query.new_name.clone(), true);
+ bucket_p.aliases = LwwMap::raw_item(query.new_name.clone(), alias_ts, true);
self.garage.bucket_table.insert(&bucket).await?;
Ok(AdminRpc::Ok(format!(
@@ -336,14 +367,14 @@ impl AdminRpcHandler {
.bucket_helper()
.get_existing_bucket(bucket_id)
.await?;
- let mut bucket_state = bucket.state.as_option_mut().unwrap();
+ let mut bucket_p = bucket.state.as_option_mut().unwrap();
- let has_other_aliases = bucket_state
+ let has_other_aliases = bucket_p
.aliases
.items()
.iter()
.any(|(_, _, active)| *active)
- || bucket_state
+ || bucket_p
.local_aliases
.items()
.iter()
@@ -352,15 +383,22 @@ impl AdminRpcHandler {
return Err(Error::Message(format!("Bucket {} doesn't have other aliases, please delete it instead of just unaliasing.", query.name)));
}
+ // Checks ok, remove alias
let mut key_param = key.state.as_option_mut().unwrap();
- key_param.local_aliases = key_param
- .local_aliases
- .update_mutator(query.name.clone(), Deletable::delete());
+ let bucket_p_local_alias_key = (key.key_id.clone(), query.name.clone());
+
+ let alias_ts = increment_logical_clock_2(
+ key_param.local_aliases.get_timestamp(&query.name),
+ bucket_p
+ .local_aliases
+ .get_timestamp(&bucket_p_local_alias_key),
+ );
+
+ key_param.local_aliases =
+ LwwMap::raw_item(query.name.clone(), alias_ts, Deletable::delete());
self.garage.key_table.insert(&key).await?;
- bucket_state.local_aliases = bucket_state
- .local_aliases
- .update_mutator((key.key_id.clone(), query.name.clone()), false);
+ bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false);
self.garage.bucket_table.insert(&bucket).await?;
Ok(AdminRpc::Ok(format!(
@@ -401,12 +439,17 @@ impl AdminRpcHandler {
.get(&EmptyKey, &query.name)
.await?
.ok_or_message("Internal error: alias not found")?;
- alias.state.update(Deletable::delete());
+
+ // Checks ok, remove alias
+ let alias_ts = increment_logical_clock_2(
+ alias.state.timestamp(),
+ bucket_state.aliases.get_timestamp(&query.name),
+ );
+
+ alias.state = Lww::raw(alias_ts, Deletable::delete());
self.garage.bucket_alias_table.insert(&alias).await?;
- bucket_state.aliases = bucket_state
- .aliases
- .update_mutator(query.name.clone(), false);
+ bucket_state.aliases = LwwMap::raw_item(query.name.clone(), alias_ts, false);
self.garage.bucket_table.insert(&bucket).await?;
Ok(AdminRpc::Ok(format!("Bucket alias {} deleted", query.name)))
@@ -494,7 +537,13 @@ impl AdminRpcHandler {
));
}
- bucket_state.website_access.update(query.allow);
+ let website = if query.allow {
+ Some(ByteBuf::from(DEFAULT_WEBSITE_CONFIGURATION.to_vec()))
+ } else {
+ None
+ };
+
+ bucket_state.website_config.update(website);
self.garage.bucket_table.insert(&bucket).await?;
let msg = if query.allow {
diff --git a/src/garage/cli/cmd.rs b/src/garage/cli/cmd.rs
index 1c64f9b5..834261e4 100644
--- a/src/garage/cli/cmd.rs
+++ b/src/garage/cli/cmd.rs
@@ -167,7 +167,7 @@ pub async fn cmd_admin(
let mut table = vec![];
for alias in bl {
if let Some(p) = alias.state.get().as_option() {
- table.push(format!("\t{}\t{:?}", alias.name, p.bucket_id));
+ table.push(format!("\t{}\t{:?}", alias.name(), p.bucket_id));
}
}
format_table(table);
diff --git a/src/garage/cli/util.rs b/src/garage/cli/util.rs
index ad48c301..b4ea14d1 100644
--- a/src/garage/cli/util.rs
+++ b/src/garage/cli/util.rs
@@ -82,7 +82,7 @@ pub fn print_bucket_info(bucket: &Bucket, relevant_keys: &HashMap<String, Key>)
match &bucket.state {
Deletable::Deleted => println!("Bucket is deleted."),
Deletable::Present(p) => {
- println!("Website access: {}", p.website_access.get());
+ println!("Website access: {}", p.website_config.get().is_some());
println!("\nGlobal aliases:");
for (alias, _, active) in p.aliases.items().iter() {