mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-16 16:58:19 +00:00
replace Option CRDT by explicit CancelingOption and MergingOption types
This commit is contained in:
@@ -35,7 +35,7 @@ mod v2 {
|
||||
pub name: crdt::Lww<String>,
|
||||
|
||||
/// The optional time of expiration of the token
|
||||
pub expiration: crdt::Lww<Option<u64>>,
|
||||
pub expiration: crdt::Lww<crdt::CancelingOption<u64>>,
|
||||
|
||||
/// The scope of the token, i.e. list of authorized admin API calls
|
||||
pub scope: crdt::Lww<AdminApiTokenScope>,
|
||||
@@ -106,7 +106,7 @@ impl AdminApiToken {
|
||||
created: now_msec(),
|
||||
token_hash: hashed_token,
|
||||
name: crdt::Lww::new(name.to_string()),
|
||||
expiration: crdt::Lww::new(None),
|
||||
expiration: crdt::Lww::new(None.into()),
|
||||
scope: crdt::Lww::new(AdminApiTokenScope(vec!["*".to_string()])),
|
||||
}),
|
||||
};
|
||||
@@ -147,9 +147,9 @@ impl AdminApiToken {
|
||||
|
||||
impl AdminApiTokenParams {
|
||||
pub fn is_expired(&self, ts_now: u64) -> bool {
|
||||
match *self.expiration.get() {
|
||||
match self.expiration.get().inner() {
|
||||
None => false,
|
||||
Some(exp) => ts_now >= exp,
|
||||
Some(exp) => ts_now >= *exp,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ mod v08 {
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BucketAlias {
|
||||
pub(super) name: String,
|
||||
pub state: crdt::Lww<Option<Uuid>>,
|
||||
pub state: crdt::Lww<crdt::CancelingOption<Uuid>>,
|
||||
}
|
||||
|
||||
impl garage_util::migrate::InitialFormat for BucketAlias {}
|
||||
@@ -25,12 +25,12 @@ impl BucketAlias {
|
||||
pub fn new(name: String, ts: u64, bucket_id: Option<Uuid>) -> Self {
|
||||
BucketAlias {
|
||||
name,
|
||||
state: crdt::Lww::raw(ts, bucket_id),
|
||||
state: crdt::Lww::raw(ts, CancelingOption(bucket_id)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_deleted(&self) -> bool {
|
||||
self.state.get().is_none()
|
||||
self.state.get().inner().is_none()
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
|
||||
@@ -45,12 +45,12 @@ mod v08 {
|
||||
/// Whether this bucket is allowed for website access
|
||||
/// (under all of its global alias names),
|
||||
/// and if so, the website configuration XML document
|
||||
pub website_config: crdt::Lww<Option<WebsiteConfig>>,
|
||||
pub website_config: crdt::Lww<crdt::CancelingOption<WebsiteConfig>>,
|
||||
/// CORS rules
|
||||
pub cors_config: crdt::Lww<Option<Vec<CorsRule>>>,
|
||||
pub cors_config: crdt::Lww<crdt::CancelingOption<Vec<CorsRule>>>,
|
||||
/// Lifecycle configuration
|
||||
#[serde(default)]
|
||||
pub lifecycle_config: crdt::Lww<Option<Vec<LifecycleRule>>>,
|
||||
pub lifecycle_config: crdt::Lww<crdt::CancelingOption<Vec<LifecycleRule>>>,
|
||||
/// Bucket quotas
|
||||
#[serde(default)]
|
||||
pub quotas: crdt::Lww<BucketQuotas>,
|
||||
@@ -164,11 +164,11 @@ mod v2 {
|
||||
/// Whether this bucket is allowed for website access
|
||||
/// (under all of its global alias names),
|
||||
/// and if so, the website configuration XML document
|
||||
pub website_config: crdt::Lww<Option<WebsiteConfig>>,
|
||||
pub website_config: crdt::Lww<crdt::CancelingOption<WebsiteConfig>>,
|
||||
/// CORS rules
|
||||
pub cors_config: crdt::Lww<Option<Vec<CorsRule>>>,
|
||||
pub cors_config: crdt::Lww<crdt::CancelingOption<Vec<CorsRule>>>,
|
||||
/// Lifecycle configuration
|
||||
pub lifecycle_config: crdt::Lww<Option<Vec<LifecycleRule>>>,
|
||||
pub lifecycle_config: crdt::Lww<crdt::CancelingOption<Vec<LifecycleRule>>>,
|
||||
/// Bucket quotas
|
||||
pub quotas: crdt::Lww<BucketQuotas>,
|
||||
}
|
||||
@@ -259,9 +259,9 @@ impl BucketParams {
|
||||
authorized_keys: crdt::Map::new(),
|
||||
aliases: crdt::LwwMap::new(),
|
||||
local_aliases: crdt::LwwMap::new(),
|
||||
website_config: crdt::Lww::new(None),
|
||||
cors_config: crdt::Lww::new(None),
|
||||
lifecycle_config: crdt::Lww::new(None),
|
||||
website_config: crdt::Lww::new(None.into()),
|
||||
cors_config: crdt::Lww::new(None.into()),
|
||||
lifecycle_config: crdt::Lww::new(None.into()),
|
||||
quotas: crdt::Lww::new(BucketQuotas::default()),
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -52,7 +52,7 @@ impl<'a> BucketHelper<'a> {
|
||||
.0
|
||||
.bucket_alias_table
|
||||
.get_local(&EmptyKey, bucket_name)?
|
||||
.and_then(|x| *x.state.get());
|
||||
.and_then(|x| x.state.get().into_inner());
|
||||
match alias {
|
||||
Some(id) => id,
|
||||
None => return Ok(None),
|
||||
@@ -91,15 +91,18 @@ impl<'a> BucketHelper<'a> {
|
||||
.as_option()
|
||||
.ok_or_message("Key should not be deleted at this point")?;
|
||||
|
||||
let bucket_opt =
|
||||
if let Some(Some(bucket_id)) = api_key_params.local_aliases.get(bucket_name) {
|
||||
self.0
|
||||
.bucket_table
|
||||
.get_local(&EmptyKey, bucket_id)?
|
||||
.filter(|x| !x.state.is_deleted())
|
||||
} else {
|
||||
self.resolve_global_bucket_fast(bucket_name)?
|
||||
};
|
||||
let bucket_opt = if let Some(bucket_id) = api_key_params
|
||||
.local_aliases
|
||||
.get(bucket_name)
|
||||
.and_then(|x| x.inner())
|
||||
{
|
||||
self.0
|
||||
.bucket_table
|
||||
.get_local(&EmptyKey, bucket_id)?
|
||||
.filter(|x| !x.state.is_deleted())
|
||||
} else {
|
||||
self.resolve_global_bucket_fast(bucket_name)?
|
||||
};
|
||||
bucket_opt.ok_or_else(|| Error::NoSuchBucket(bucket_name.to_string()))
|
||||
}
|
||||
|
||||
@@ -125,7 +128,7 @@ impl<'a> BucketHelper<'a> {
|
||||
.bucket_alias_table
|
||||
.get(&EmptyKey, bucket_name)
|
||||
.await?
|
||||
.and_then(|x| *x.state.get());
|
||||
.and_then(|x| x.state.get().into_inner());
|
||||
match alias {
|
||||
Some(id) => id,
|
||||
None => return Ok(None),
|
||||
@@ -163,8 +166,7 @@ impl<'a> BucketHelper<'a> {
|
||||
.ok_or_else(|| GarageError::Message(format!("access key {} has been deleted", key_id)))?
|
||||
.local_aliases
|
||||
.get(bucket_name)
|
||||
.copied()
|
||||
.flatten();
|
||||
.and_then(|x| x.inner().copied());
|
||||
|
||||
if let Some(bucket_id) = local_alias {
|
||||
Ok(self
|
||||
|
||||
+34
-18
@@ -74,8 +74,8 @@ impl<'a> LockedHelper<'a> {
|
||||
let alias = self.0.bucket_alias_table.get(&EmptyKey, alias_name).await?;
|
||||
|
||||
if let Some(existing_alias) = alias.as_ref() {
|
||||
if let Some(p_bucket) = existing_alias.state.get() {
|
||||
if *p_bucket != bucket_id {
|
||||
if let Some(p_bucket) = existing_alias.state.get().into_inner() {
|
||||
if p_bucket != bucket_id {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Alias {} already exists and points to different bucket: {:?}",
|
||||
alias_name, p_bucket
|
||||
@@ -98,7 +98,7 @@ impl<'a> LockedHelper<'a> {
|
||||
let alias = match alias {
|
||||
None => BucketAlias::new(alias_name.clone(), alias_ts, Some(bucket_id)),
|
||||
Some(mut a) => {
|
||||
a.state = Lww::raw(alias_ts, Some(bucket_id));
|
||||
a.state = Lww::raw(alias_ts, Some(bucket_id).into());
|
||||
a
|
||||
}
|
||||
};
|
||||
@@ -128,7 +128,13 @@ impl<'a> LockedHelper<'a> {
|
||||
.bucket_alias_table
|
||||
.get(&EmptyKey, alias_name)
|
||||
.await?
|
||||
.filter(|a| a.state.get().map(|x| x == bucket_id).unwrap_or(false))
|
||||
.filter(|a| {
|
||||
a.state
|
||||
.get()
|
||||
.into_inner()
|
||||
.map(|x| x == bucket_id)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.ok_or_message(format!(
|
||||
"Internal error: alias not found or does not point to bucket {:?}",
|
||||
bucket_id
|
||||
@@ -157,7 +163,7 @@ impl<'a> LockedHelper<'a> {
|
||||
// ---- timestamp-ensured causality barrier ----
|
||||
// writes are now done and all writes use timestamp alias_ts
|
||||
|
||||
alias.state = Lww::raw(alias_ts, None);
|
||||
alias.state = Lww::raw(alias_ts, None.into());
|
||||
self.0.bucket_alias_table.insert(&alias).await?;
|
||||
|
||||
bucket_state.aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, false);
|
||||
@@ -199,8 +205,8 @@ impl<'a> LockedHelper<'a> {
|
||||
// ---- 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);
|
||||
if alias.state.get().inner() == Some(&bucket_id) {
|
||||
alias.state = Lww::raw(alias_ts, None.into());
|
||||
self.0.bucket_alias_table.insert(&alias).await?;
|
||||
}
|
||||
|
||||
@@ -237,7 +243,11 @@ impl<'a> LockedHelper<'a> {
|
||||
|
||||
let key_param = key.state.as_option_mut().unwrap();
|
||||
|
||||
if let Some(Some(existing_alias)) = key_param.local_aliases.get(alias_name) {
|
||||
if let Some(Some(existing_alias)) = key_param
|
||||
.local_aliases
|
||||
.get(alias_name)
|
||||
.map(CancelingOption::inner)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
@@ -261,7 +271,8 @@ impl<'a> LockedHelper<'a> {
|
||||
// ---- timestamp-ensured causality barrier ----
|
||||
// writes are now done and all writes use timestamp alias_ts
|
||||
|
||||
key_param.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, Some(bucket_id));
|
||||
key_param.local_aliases =
|
||||
LwwMap::raw_item(alias_name.clone(), alias_ts, Some(bucket_id).into());
|
||||
self.0.key_table.insert(&key).await?;
|
||||
|
||||
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, true);
|
||||
@@ -288,7 +299,12 @@ impl<'a> LockedHelper<'a> {
|
||||
let key_p = key.state.as_option().unwrap();
|
||||
let bucket_p = bucket.state.as_option_mut().unwrap();
|
||||
|
||||
if key_p.local_aliases.get(alias_name).cloned().flatten() != Some(bucket_id) {
|
||||
if key_p
|
||||
.local_aliases
|
||||
.get(alias_name)
|
||||
.and_then(CancelingOption::inner)
|
||||
!= Some(&bucket_id)
|
||||
{
|
||||
return Err(GarageError::Message(format!(
|
||||
"Bucket {:?} does not have alias {} in namespace of key {}",
|
||||
bucket_id, alias_name, key_id
|
||||
@@ -325,7 +341,7 @@ impl<'a> LockedHelper<'a> {
|
||||
// writes are now done and all writes use timestamp alias_ts
|
||||
|
||||
key.state.as_option_mut().unwrap().local_aliases =
|
||||
LwwMap::raw_item(alias_name.clone(), alias_ts, None);
|
||||
LwwMap::raw_item(alias_name.clone(), alias_ts, None.into());
|
||||
self.0.key_table.insert(&key).await?;
|
||||
|
||||
bucket_p.local_aliases = LwwMap::raw_item(bucket_p_local_alias_key, alias_ts, false);
|
||||
@@ -367,7 +383,7 @@ impl<'a> LockedHelper<'a> {
|
||||
// writes are now done and all writes use timestamp alias_ts
|
||||
|
||||
if let Some(kp) = key.state.as_option_mut() {
|
||||
kp.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, None);
|
||||
kp.local_aliases = LwwMap::raw_item(alias_name.clone(), alias_ts, None.into());
|
||||
self.0.key_table.insert(&key).await?;
|
||||
}
|
||||
|
||||
@@ -444,8 +460,8 @@ impl<'a> LockedHelper<'a> {
|
||||
|
||||
// 1. Delete local aliases
|
||||
for (alias, _, to) in state.local_aliases.items().iter() {
|
||||
if let Some(bucket_id) = to {
|
||||
self.purge_local_bucket_alias(*bucket_id, &key.key_id, alias)
|
||||
if let Some(bucket_id) = to.into_inner() {
|
||||
self.purge_local_bucket_alias(bucket_id, &key.key_id, alias)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
@@ -501,7 +517,7 @@ impl<'a> LockedHelper<'a> {
|
||||
.data
|
||||
.decode_entry(&(item?.1))
|
||||
.map_err(db::TxError::Abort)?;
|
||||
if let Some(id) = alias.state.get() {
|
||||
if let Some(id) = alias.state.get().inner() {
|
||||
if all_buckets.contains(id) {
|
||||
// keep aliases
|
||||
global_aliases.insert(alias.name().to_string(), *id);
|
||||
@@ -512,7 +528,7 @@ impl<'a> LockedHelper<'a> {
|
||||
alias.name(),
|
||||
id
|
||||
);
|
||||
alias.state.update(None);
|
||||
alias.state.update(None.into());
|
||||
delete_global.push(alias);
|
||||
}
|
||||
}
|
||||
@@ -544,7 +560,7 @@ impl<'a> LockedHelper<'a> {
|
||||
};
|
||||
let mut has_changes = false;
|
||||
for (name, _, to) in p.local_aliases.items().to_vec() {
|
||||
if let Some(id) = to {
|
||||
if let Some(id) = to.into_inner() {
|
||||
if all_buckets.contains(&id) {
|
||||
local_aliases.insert((key.key_id.clone(), name), id);
|
||||
} else {
|
||||
@@ -552,7 +568,7 @@ impl<'a> LockedHelper<'a> {
|
||||
"local alias: remove ({}, {}) -> {:?} (bucket is deleted)",
|
||||
key.key_id, name, id
|
||||
);
|
||||
p.local_aliases.update_in_place(name, None);
|
||||
p.local_aliases.update_in_place(name, None.into());
|
||||
has_changes = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ mod v08 {
|
||||
|
||||
/// A key can have a local view of buckets names it is
|
||||
/// the only one to see, this is the namespace for these aliases
|
||||
pub local_aliases: crdt::LwwMap<String, Option<Uuid>>,
|
||||
pub local_aliases: crdt::LwwMap<String, crdt::CancelingOption<Uuid>>,
|
||||
}
|
||||
|
||||
impl garage_util::migrate::InitialFormat for Key {}
|
||||
@@ -79,7 +79,7 @@ mod v2 {
|
||||
/// Name for the key
|
||||
pub name: crdt::Lww<String>,
|
||||
/// The optional time of expiration of the key
|
||||
pub expiration: crdt::Lww<Option<u64>>,
|
||||
pub expiration: crdt::Lww<crdt::CancelingOption<u64>>,
|
||||
|
||||
/// Flag to allow users having this key to create buckets
|
||||
pub allow_create_bucket: crdt::Lww<bool>,
|
||||
@@ -91,7 +91,7 @@ mod v2 {
|
||||
|
||||
/// A key can have a local view of buckets names it is
|
||||
/// the only one to see, this is the namespace for these aliases
|
||||
pub local_aliases: crdt::LwwMap<String, Option<Uuid>>,
|
||||
pub local_aliases: crdt::LwwMap<String, crdt::CancelingOption<Uuid>>,
|
||||
}
|
||||
|
||||
impl garage_util::migrate::Migrate for Key {
|
||||
@@ -106,7 +106,7 @@ mod v2 {
|
||||
created: None,
|
||||
secret_key: x.secret_key,
|
||||
name: x.name,
|
||||
expiration: crdt::Lww::raw(0, None),
|
||||
expiration: crdt::Lww::raw(0, None.into()),
|
||||
allow_create_bucket: x.allow_create_bucket,
|
||||
authorized_buckets: x.authorized_buckets,
|
||||
local_aliases: x.local_aliases,
|
||||
@@ -124,7 +124,7 @@ impl KeyParams {
|
||||
created: Some(now_msec()),
|
||||
secret_key: secret_key.to_string(),
|
||||
name: crdt::Lww::new(name.to_string()),
|
||||
expiration: crdt::Lww::new(None),
|
||||
expiration: crdt::Lww::new(None.into()),
|
||||
allow_create_bucket: crdt::Lww::new(false),
|
||||
authorized_buckets: crdt::Map::new(),
|
||||
local_aliases: crdt::LwwMap::new(),
|
||||
@@ -229,9 +229,9 @@ impl Key {
|
||||
|
||||
impl KeyParams {
|
||||
pub fn is_expired(&self, ts_now: u64) -> bool {
|
||||
match *self.expiration.get() {
|
||||
match self.expiration.get().inner() {
|
||||
None => false,
|
||||
Some(exp) => ts_now >= exp,
|
||||
Some(exp) => ts_now >= *exp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ async fn process_object(
|
||||
let lifecycle_policy: &[LifecycleRule] = bucket
|
||||
.state
|
||||
.as_option()
|
||||
.and_then(|s| s.lifecycle_config.get().as_deref())
|
||||
.and_then(|s| s.lifecycle_config.get().inner().map(|x| &x[..]))
|
||||
.unwrap_or_default();
|
||||
|
||||
if lifecycle_policy.iter().all(|x| !x.enabled) {
|
||||
|
||||
Reference in New Issue
Block a user