New model for buckets #172
12 changed files with 86 additions and 62 deletions
|
@ -7,7 +7,6 @@ use hyper::server::conn::AddrStream;
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use hyper::{Body, Request, Response, Server};
|
use hyper::{Body, Request, Response, Server};
|
||||||
|
|
||||||
use garage_util::crdt;
|
|
||||||
use garage_util::data::*;
|
use garage_util::data::*;
|
||||||
use garage_util::error::Error as GarageError;
|
use garage_util::error::Error as GarageError;
|
||||||
|
|
||||||
|
@ -306,8 +305,7 @@ async fn resolve_bucket(
|
||||||
.as_option()
|
.as_option()
|
||||||
.ok_or_else(|| Error::Forbidden("Operation is not allowed for this key.".to_string()))?;
|
.ok_or_else(|| Error::Forbidden("Operation is not allowed for this key.".to_string()))?;
|
||||||
|
|
||||||
if let Some(crdt::Deletable::Present(bucket_id)) = api_key_params.local_aliases.get(bucket_name)
|
if let Some(Some(bucket_id)) = api_key_params.local_aliases.get(bucket_name) {
|
||||||
{
|
|
||||||
Ok(*bucket_id)
|
Ok(*bucket_id)
|
||||||
} else {
|
} else {
|
||||||
Ok(garage
|
Ok(garage
|
||||||
|
|
|
@ -65,8 +65,8 @@ pub async fn handle_list_buckets(garage: &Garage, api_key: &Key) -> Result<Respo
|
||||||
if *active {
|
if *active {
|
||||||
let alias_ent = garage.bucket_alias_table.get(&EmptyKey, alias).await?;
|
let alias_ent = garage.bucket_alias_table.get(&EmptyKey, alias).await?;
|
||||||
if let Some(alias_ent) = alias_ent {
|
if let Some(alias_ent) = alias_ent {
|
||||||
if let Some(alias_p) = alias_ent.state.get().as_option() {
|
if let Some(alias_bucket) = alias_ent.state.get() {
|
||||||
if alias_p.bucket_id == *bucket_id {
|
if alias_bucket == bucket_id {
|
||||||
aliases.insert(alias_ent.name().to_string(), *bucket_id);
|
aliases.insert(alias_ent.name().to_string(), *bucket_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,8 @@ pub async fn handle_list_buckets(garage: &Garage, api_key: &Key) -> Result<Respo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (alias, _, id) in key_state.local_aliases.items() {
|
for (alias, _, id_opt) in key_state.local_aliases.items() {
|
||||||
if let Some(id) = id.as_option() {
|
if let Some(id) = id_opt {
|
||||||
aliases.insert(alias.clone(), *id);
|
aliases.insert(alias.clone(), *id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ impl AdminRpcHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(alias) = self.garage.bucket_alias_table.get(&EmptyKey, name).await? {
|
if let Some(alias) = self.garage.bucket_alias_table.get(&EmptyKey, name).await? {
|
||||||
if !alias.state.get().is_deleted() {
|
if alias.state.get().is_some() {
|
||||||
return Err(Error::BadRequest(format!("Bucket {} already exists", name)));
|
return Err(Error::BadRequest(format!("Bucket {} already exists", name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ impl AdminRpcHandler {
|
||||||
// 2. delete bucket alias
|
// 2. delete bucket alias
|
||||||
if bucket_alias.is_some() {
|
if bucket_alias.is_some() {
|
||||||
helper
|
helper
|
||||||
.unset_global_bucket_alias(bucket_id, &query.name)
|
.purge_global_bucket_alias(bucket_id, &query.name)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ impl AdminRpcHandler {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.local_aliases
|
.local_aliases
|
||||||
.get(&query.name)
|
.get(&query.name)
|
||||||
.map(|a| a.into_option())
|
.cloned()
|
||||||
.flatten()
|
.flatten()
|
||||||
.ok_or_bad_request("Bucket not found")?;
|
.ok_or_bad_request("Bucket not found")?;
|
||||||
|
|
||||||
|
@ -484,20 +484,26 @@ impl AdminRpcHandler {
|
||||||
let state = key.state.as_option_mut().unwrap();
|
let state = key.state.as_option_mut().unwrap();
|
||||||
|
|
||||||
// --- done checking, now commit ---
|
// --- done checking, now commit ---
|
||||||
|
// (the step at unset_local_bucket_alias will fail if a bucket
|
||||||
|
// does not have another alias, the deletion will be
|
||||||
|
// interrupted in the middle if that happens)
|
||||||
|
|
||||||
// 1. Delete local aliases
|
// 1. Delete local aliases
|
||||||
for (alias, _, to) in state.local_aliases.items().iter() {
|
for (alias, _, to) in state.local_aliases.items().iter() {
|
||||||
if let Deletable::Present(bucket_id) = to {
|
if let Some(bucket_id) = to {
|
||||||
helper
|
helper
|
||||||
.unset_local_bucket_alias(*bucket_id, &key.key_id, alias)
|
.unset_local_bucket_alias(*bucket_id, &key.key_id, alias)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. Delete authorized buckets
|
|
||||||
|
// 2. Remove permissions on all authorized buckets
|
||||||
for (ab_id, _auth) in state.authorized_buckets.items().iter() {
|
for (ab_id, _auth) in state.authorized_buckets.items().iter() {
|
||||||
helper
|
helper
|
||||||
.set_bucket_key_permissions(*ab_id, &key.key_id, BucketKeyPerm::no_permissions())
|
.set_bucket_key_permissions(*ab_id, &key.key_id, BucketKeyPerm::no_permissions())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Actually delete key
|
// 3. Actually delete key
|
||||||
key.state = Deletable::delete();
|
key.state = Deletable::delete();
|
||||||
self.garage.key_table.insert(&key).await?;
|
self.garage.key_table.insert(&key).await?;
|
||||||
|
|
|
@ -168,8 +168,8 @@ pub async fn cmd_admin(
|
||||||
println!("List of buckets:");
|
println!("List of buckets:");
|
||||||
let mut table = vec![];
|
let mut table = vec![];
|
||||||
for alias in bl {
|
for alias in bl {
|
||||||
if let Some(p) = alias.state.get().as_option() {
|
if let Some(alias_bucket) = alias.state.get() {
|
||||||
table.push(format!("\t{}\t{:?}", alias.name(), p.bucket_id));
|
table.push(format!("\t{}\t{:?}", alias.name(), alias_bucket));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format_table(table);
|
format_table(table);
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub fn print_key_info(key: &Key, relevant_buckets: &HashMap<Uuid, Bucket>) {
|
||||||
println!("\nKey-specific bucket aliases:");
|
println!("\nKey-specific bucket aliases:");
|
||||||
let mut table = vec![];
|
let mut table = vec![];
|
||||||
for (alias_name, _, alias) in p.local_aliases.items().iter() {
|
for (alias_name, _, alias) in p.local_aliases.items().iter() {
|
||||||
if let Some(bucket_id) = alias.as_option() {
|
if let Some(bucket_id) = alias {
|
||||||
table.push(format!(
|
table.push(format!(
|
||||||
"\t{}\t{}\t{}",
|
"\t{}\t{}\t{}",
|
||||||
alias_name,
|
alias_name,
|
||||||
|
@ -55,7 +55,7 @@ pub fn print_key_info(key: &Key, relevant_buckets: &HashMap<Uuid, Bucket>) {
|
||||||
.local_aliases
|
.local_aliases
|
||||||
.items()
|
.items()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, _, a)| a.as_option() == Some(bucket_id))
|
.filter(|(_, _, a)| *a == Some(*bucket_id))
|
||||||
.map(|(a, _, _)| a.clone())
|
.map(|(a, _, _)| a.clone())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
|
@ -10,32 +10,23 @@ use garage_table::*;
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct BucketAlias {
|
pub struct BucketAlias {
|
||||||
name: String,
|
name: String,
|
||||||
pub state: crdt::Lww<crdt::Deletable<AliasParams>>,
|
pub state: crdt::Lww<Option<Uuid>>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct AliasParams {
|
|
||||||
pub bucket_id: Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AutoCrdt for AliasParams {
|
|
||||||
const WARN_IF_DIFFERENT: bool = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BucketAlias {
|
impl BucketAlias {
|
||||||
pub fn new(name: String, ts: u64, bucket_id: Uuid) -> Option<Self> {
|
pub fn new(name: String, ts: u64, bucket_id: Option<Uuid>) -> Option<Self> {
|
||||||
if !is_valid_bucket_name(&name) {
|
if !is_valid_bucket_name(&name) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(BucketAlias {
|
Some(BucketAlias {
|
||||||
name,
|
name,
|
||||||
state: crdt::Lww::raw(ts, crdt::Deletable::present(AliasParams { bucket_id })),
|
state: crdt::Lww::raw(ts, bucket_id),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_deleted(&self) -> bool {
|
pub fn is_deleted(&self) -> bool {
|
||||||
self.state.get().is_deleted()
|
self.state.get().is_none()
|
||||||
}
|
}
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
|
|
|
@ -63,6 +63,7 @@ impl BucketParams {
|
||||||
|
|
||||||
impl Crdt for BucketParams {
|
impl Crdt for BucketParams {
|
||||||
fn merge(&mut self, o: &Self) {
|
fn merge(&mut self, o: &Self) {
|
||||||
|
self.creation_date = std::cmp::min(self.creation_date, o.creation_date);
|
||||||
self.authorized_keys.merge(&o.authorized_keys);
|
self.authorized_keys.merge(&o.authorized_keys);
|
||||||
self.website_config.merge(&o.website_config);
|
self.website_config.merge(&o.website_config);
|
||||||
self.aliases.merge(&o.aliases);
|
self.aliases.merge(&o.aliases);
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
.bucket_alias_table
|
.bucket_alias_table
|
||||||
.get(&EmptyKey, bucket_name)
|
.get(&EmptyKey, bucket_name)
|
||||||
.await?
|
.await?
|
||||||
.map(|x| x.state.get().as_option().map(|x| x.bucket_id))
|
.map(|x| *x.state.get())
|
||||||
.flatten())
|
.flatten())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,11 +154,11 @@ impl<'a> BucketHelper<'a> {
|
||||||
let alias = self.0.bucket_alias_table.get(&EmptyKey, alias_name).await?;
|
let alias = self.0.bucket_alias_table.get(&EmptyKey, alias_name).await?;
|
||||||
|
|
||||||
if let Some(existing_alias) = alias.as_ref() {
|
if let Some(existing_alias) = alias.as_ref() {
|
||||||
if let Some(p) = existing_alias.state.get().as_option() {
|
if let Some(p_bucket) = existing_alias.state.get() {
|
||||||
if p.bucket_id != bucket_id {
|
if *p_bucket != bucket_id {
|
||||||
return Err(Error::BadRequest(format!(
|
return Err(Error::BadRequest(format!(
|
||||||
"Alias {} already exists and points to different bucket: {:?}",
|
"Alias {} already exists and points to different bucket: {:?}",
|
||||||
alias_name, p.bucket_id
|
alias_name, p_bucket
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,10 +176,10 @@ impl<'a> BucketHelper<'a> {
|
||||||
// writes are now done and all writes use timestamp alias_ts
|
// writes are now done and all writes use timestamp alias_ts
|
||||||
|
|
||||||
let alias = match alias {
|
let alias = match alias {
|
||||||
None => BucketAlias::new(alias_name.clone(), alias_ts, bucket_id)
|
None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id))
|
||||||
.ok_or_bad_request(format!("{}: {}", alias_name, INVALID_BUCKET_NAME_MESSAGE))?,
|
.ok_or_bad_request(format!("{}: {}", alias_name, INVALID_BUCKET_NAME_MESSAGE))?,
|
||||||
Some(mut a) => {
|
Some(mut a) => {
|
||||||
a.state = Lww::raw(alias_ts, Deletable::present(AliasParams { bucket_id }));
|
a.state = Lww::raw(alias_ts, Some(bucket_id));
|
||||||
a
|
a
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -209,13 +209,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
.bucket_alias_table
|
.bucket_alias_table
|
||||||
.get(&EmptyKey, alias_name)
|
.get(&EmptyKey, alias_name)
|
||||||
.await?
|
.await?
|
||||||
.filter(|a| {
|
.filter(|a| a.state.get().map(|x| x == bucket_id).unwrap_or(false))
|
||||||
a.state
|
|
||||||
.get()
|
|
||||||
.as_option()
|
|
||||||
.map(|x| x.bucket_id == bucket_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.ok_or_message(format!(
|
.ok_or_message(format!(
|
||||||
"Internal error: alias not found or does not point to bucket {:?}",
|
"Internal error: alias not found or does not point to bucket {:?}",
|
||||||
bucket_id
|
bucket_id
|
||||||
|
@ -244,7 +238,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
// ---- timestamp-ensured causality barrier ----
|
// ---- timestamp-ensured causality barrier ----
|
||||||
// writes are now done and all writes use timestamp alias_ts
|
// writes are now done and all writes use timestamp alias_ts
|
||||||
|
|
||||||
alias.state = Lww::raw(alias_ts, Deletable::delete());
|
alias.state = Lww::raw(alias_ts, None);
|
||||||
self.0.bucket_alias_table.insert(&alias).await?;
|
self.0.bucket_alias_table.insert(&alias).await?;
|
||||||
|
|
||||||
bucket_state.aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, false);
|
bucket_state.aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, false);
|
||||||
|
@ -253,6 +247,51 @@ impl<'a> BucketHelper<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures a bucket does not have a certain global alias.
|
||||||
|
/// Contrarily to unset_global_bucket_alias, this does not
|
||||||
|
/// fail on any condition other than:
|
||||||
|
/// - bucket cannot be found (its fine if it is in deleted state)
|
||||||
|
/// - alias cannot be found (its fine if it points to nothing or
|
||||||
|
/// to another bucket)
|
||||||
|
pub async fn purge_global_bucket_alias(
|
||||||
|
&self,
|
||||||
|
bucket_id: Uuid,
|
||||||
|
alias_name: &String,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut bucket = self.get_internal_bucket(bucket_id).await?;
|
||||||
|
|
||||||
|
let mut alias = self
|
||||||
|
.0
|
||||||
|
.bucket_alias_table
|
||||||
|
.get(&EmptyKey, alias_name)
|
||||||
|
.await?
|
||||||
|
.ok_or_message(format!("Alias {} not found", alias_name))?;
|
||||||
|
|
||||||
|
// Checks ok, remove alias
|
||||||
|
let alias_ts = match bucket.state.as_option() {
|
||||||
|
Some(bucket_state) => increment_logical_clock_2(
|
||||||
|
alias.state.timestamp(),
|
||||||
|
bucket_state.aliases.get_timestamp(alias_name),
|
||||||
|
),
|
||||||
|
None => increment_logical_clock(alias.state.timestamp()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- timestamp-ensured causality barrier ----
|
||||||
|
// writes are now done and all writes use timestamp alias_ts
|
||||||
|
|
||||||
|
if alias.state.get() == &Some(bucket_id) {
|
||||||
|
alias.state = Lww::raw(alias_ts, None);
|
||||||
|
self.0.bucket_alias_table.insert(&alias).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut bucket_state) = bucket.state.as_option_mut() {
|
||||||
|
bucket_state.aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, false);
|
||||||
|
self.0.bucket_table.insert(&bucket).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets a new alias for a bucket in the local namespace of a key.
|
/// Sets a new alias for a bucket in the local namespace of a key.
|
||||||
/// This function fails if:
|
/// This function fails if:
|
||||||
/// - alias name is not valid according to S3 spec
|
/// - alias name is not valid according to S3 spec
|
||||||
|
@ -277,7 +316,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
|
|
||||||
let mut key_param = key.state.as_option_mut().unwrap();
|
let mut key_param = key.state.as_option_mut().unwrap();
|
||||||
|
|
||||||
if let Some(Deletable::Present(existing_alias)) = key_param.local_aliases.get(alias_name) {
|
if let Some(Some(existing_alias)) = key_param.local_aliases.get(alias_name) {
|
||||||
if *existing_alias != bucket_id {
|
if *existing_alias != bucket_id {
|
||||||
return Err(Error::BadRequest(format!("Alias {} already exists in namespace of key {} and points to different bucket: {:?}", alias_name, key.key_id, existing_alias)));
|
return Err(Error::BadRequest(format!("Alias {} already exists in namespace of key {} and points to different bucket: {:?}", alias_name, key.key_id, existing_alias)));
|
||||||
}
|
}
|
||||||
|
@ -301,8 +340,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
// ---- timestamp-ensured causality barrier ----
|
// ---- timestamp-ensured causality barrier ----
|
||||||
// writes are now done and all writes use timestamp alias_ts
|
// writes are now done and all writes use timestamp alias_ts
|
||||||
|
|
||||||
key_param.local_aliases =
|
key_param.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, Some(bucket_id));
|
||||||
LwwMap::raw_item(alias_name.clone(), alias_ts, Deletable::present(bucket_id));
|
|
||||||
self.0.key_table.insert(&key).await?;
|
self.0.key_table.insert(&key).await?;
|
||||||
|
|
||||||
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, true);
|
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, true);
|
||||||
|
@ -334,8 +372,8 @@ impl<'a> BucketHelper<'a> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.local_aliases
|
.local_aliases
|
||||||
.get(alias_name)
|
.get(alias_name)
|
||||||
.map(|x| x.as_option())
|
.cloned()
|
||||||
.flatten() != Some(&bucket_id)
|
.flatten() != Some(bucket_id)
|
||||||
{
|
{
|
||||||
return Err(GarageError::Message(format!(
|
return Err(GarageError::Message(format!(
|
||||||
"Bucket {:?} does not have alias {} in namespace of key {}",
|
"Bucket {:?} does not have alias {} in namespace of key {}",
|
||||||
|
@ -372,8 +410,7 @@ impl<'a> BucketHelper<'a> {
|
||||||
// ---- timestamp-ensured causality barrier ----
|
// ---- timestamp-ensured causality barrier ----
|
||||||
// writes are now done and all writes use timestamp alias_ts
|
// writes are now done and all writes use timestamp alias_ts
|
||||||
|
|
||||||
key_param.local_aliases =
|
key_param.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, None);
|
||||||
LwwMap::raw_item(alias_name.clone(), alias_ts, Deletable::delete());
|
|
||||||
self.0.key_table.insert(&key).await?;
|
self.0.key_table.insert(&key).await?;
|
||||||
|
|
||||||
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false);
|
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false);
|
||||||
|
|
|
@ -31,7 +31,7 @@ pub struct Key {
|
||||||
pub struct KeyParams {
|
pub struct KeyParams {
|
||||||
pub allow_create_bucket: crdt::Lww<bool>,
|
pub allow_create_bucket: crdt::Lww<bool>,
|
||||||
pub authorized_buckets: crdt::Map<Uuid, BucketKeyPerm>,
|
pub authorized_buckets: crdt::Map<Uuid, BucketKeyPerm>,
|
||||||
pub local_aliases: crdt::LwwMap<String, crdt::Deletable<Uuid>>,
|
pub local_aliases: crdt::LwwMap<String, Option<Uuid>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyParams {
|
impl KeyParams {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::data::*;
|
|
||||||
|
|
||||||
/// Definition of a CRDT - all CRDT Rust types implement this.
|
/// Definition of a CRDT - all CRDT Rust types implement this.
|
||||||
///
|
///
|
||||||
/// A CRDT is defined as a merge operator that respects a certain set of axioms.
|
/// A CRDT is defined as a merge operator that respects a certain set of axioms.
|
||||||
|
@ -87,7 +85,3 @@ impl AutoCrdt for String {
|
||||||
impl AutoCrdt for bool {
|
impl AutoCrdt for bool {
|
||||||
const WARN_IF_DIFFERENT: bool = true;
|
const WARN_IF_DIFFERENT: bool = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutoCrdt for FixedBytes32 {
|
|
||||||
const WARN_IF_DIFFERENT: bool = true;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// An array of 32 bytes
|
/// An array of 32 bytes
|
||||||
#[derive(Default, PartialOrd, Ord, Clone, Hash, PartialEq, Copy)]
|
#[derive(Default, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||||
pub struct FixedBytes32([u8; 32]);
|
pub struct FixedBytes32([u8; 32]);
|
||||||
|
|
||||||
impl From<[u8; 32]> for FixedBytes32 {
|
impl From<[u8; 32]> for FixedBytes32 {
|
||||||
|
@ -20,8 +20,6 @@ impl std::convert::AsRef<[u8]> for FixedBytes32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for FixedBytes32 {}
|
|
||||||
|
|
||||||
impl fmt::Debug for FixedBytes32 {
|
impl fmt::Debug for FixedBytes32 {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}…", hex::encode(&self.0[..8]))
|
write!(f, "{}…", hex::encode(&self.0[..8]))
|
||||||
|
|
|
@ -86,9 +86,8 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response<
|
||||||
.bucket_alias_table
|
.bucket_alias_table
|
||||||
.get(&EmptyKey, &bucket_name.to_string())
|
.get(&EmptyKey, &bucket_name.to_string())
|
||||||
.await?
|
.await?
|
||||||
.map(|x| x.state.take().into_option())
|
.map(|x| x.state.take())
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|param| param.bucket_id)
|
|
||||||
.ok_or(Error::NotFound)?;
|
.ok_or(Error::NotFound)?;
|
||||||
|
|
||||||
// Check bucket isn't deleted and has website access enabled
|
// Check bucket isn't deleted and has website access enabled
|
||||||
|
|
Loading…
Reference in a new issue