diff options
author | Alex Auvolat <alex@adnab.me> | 2021-12-17 11:53:13 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-01-04 12:46:41 +0100 |
commit | b1cfd16913e6957739958ef729b87c1bf3674a5d (patch) | |
tree | 781245de967e0c7a14cbdc0f2a610787f3b852d4 /src/garage | |
parent | 5db600e2316b80102e3fd4df9e8974c9586aec9c (diff) | |
download | garage-b1cfd16913e6957739958ef729b87c1bf3674a5d.tar.gz garage-b1cfd16913e6957739958ef729b87c1bf3674a5d.zip |
New buckets for 0.6.0: small fixes, including:
- ensure bucket names are correct aws s3 names
- when making aliases, ensure timestamps of links in both ways are the
same
- fix small remarks by trinity
- don't have a separate website_access field
Diffstat (limited to 'src/garage')
-rw-r--r-- | src/garage/Cargo.toml | 1 | ||||
-rw-r--r-- | src/garage/admin.rs | 155 | ||||
-rw-r--r-- | src/garage/cli/cmd.rs | 2 | ||||
-rw-r--r-- | src/garage/cli/util.rs | 2 |
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() { |