From 75ccc5a95c76f31235fcaab8a2c1795693733a4b Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 30 Aug 2023 20:02:07 +0200 Subject: [PATCH] lifecycle config: store date as given, try to debug --- src/api/s3/lifecycle.rs | 22 ++++++++++++++++------ src/model/bucket_table.rs | 16 +++++++++++++++- src/model/s3/lifecycle_worker.rs | 9 ++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/api/s3/lifecycle.rs b/src/api/s3/lifecycle.rs index 2d621eac..f0fde083 100644 --- a/src/api/s3/lifecycle.rs +++ b/src/api/s3/lifecycle.rs @@ -10,7 +10,7 @@ use crate::s3::xml::{to_xml_with_header, xmlns_tag, IntValue, Value}; use crate::signature::verify_signed_content; use garage_model::bucket_table::{ - Bucket, LifecycleExpiration as GarageLifecycleExpiration, + parse_lifecycle_date, Bucket, LifecycleExpiration as GarageLifecycleExpiration, LifecycleFilter as GarageLifecycleFilter, LifecycleRule as GarageLifecycleRule, }; use garage_model::garage::Garage; @@ -21,6 +21,8 @@ pub async fn handle_get_lifecycle(bucket: &Bucket) -> Result, Err .params() .ok_or_internal_error("Bucket should not be deleted at this point")?; + trace!("bucket: {:#?}", bucket); + if let Some(lifecycle) = param.lifecycle_config.get() { let wc = LifecycleConfiguration::from_garage_lifecycle_config(lifecycle); let xml = to_xml_with_header(&wc)?; @@ -79,7 +81,15 @@ pub async fn handle_put_lifecycle( .validate_into_garage_lifecycle_config() .ok_or_bad_request("Invalid lifecycle configuration")?; param.lifecycle_config.update(Some(config)); + garage.bucket_table.insert(&bucket).await?; + trace!("new bucket: {:#?}", bucket); + + let bucket = garage + .bucket_helper() + .get_existing_bucket(bucket_id) + .await?; + trace!("new bucket again: {:#?}", bucket); Ok(Response::builder() .status(StatusCode::OK) @@ -270,11 +280,11 @@ impl Expiration { (Some(_), Some(_)) => Err("cannot have both and in "), (None, None) => Err(" must contain either or "), (Some(days), None) => Ok(GarageLifecycleExpiration::AfterDays(days.0 as usize)), - (None, Some(date)) => date - .0 - .parse::() - .map(GarageLifecycleExpiration::AtDate) - .map_err(|_| "Invalid expiration "), + (None, Some(date)) => { + trace!("date: {}", date.0); + parse_lifecycle_date(&date.0)?; + Ok(GarageLifecycleExpiration::AtDate(date.0)) + } } } diff --git a/src/model/bucket_table.rs b/src/model/bucket_table.rs index e9d574c5..df2e9b4a 100644 --- a/src/model/bucket_table.rs +++ b/src/model/bucket_table.rs @@ -105,7 +105,7 @@ mod v08 { /// Objects expire x days after they were created AfterDays(usize), /// Objects expire at date x (must be in yyyy-mm-dd format) - AtDate(chrono::naive::NaiveDate), + AtDate(String), } #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize)] @@ -155,6 +155,20 @@ impl Crdt for BucketParams { } } +pub fn parse_lifecycle_date(date: &str) -> Result { + use chrono::prelude::*; + + if let Ok(datetime) = NaiveDateTime::parse_from_str(date, "%Y-%m-%dT%H:%M:%SZ") { + if datetime.time() == NaiveTime::MIN { + Ok(datetime.date()) + } else { + Err("date must be at midnight") + } + } else { + NaiveDate::parse_from_str(date, "%Y-%m-%d").map_err(|_| "date has invalid format") + } +} + impl Default for Bucket { fn default() -> Self { Self::new() diff --git a/src/model/s3/lifecycle_worker.rs b/src/model/s3/lifecycle_worker.rs index 02e296e7..5641b093 100644 --- a/src/model/s3/lifecycle_worker.rs +++ b/src/model/s3/lifecycle_worker.rs @@ -268,7 +268,14 @@ async fn process_object( LifecycleExpiration::AfterDays(n_days) => { (now_date - version_date) >= chrono::Duration::days(*n_days as i64) } - LifecycleExpiration::AtDate(exp_date) => now_date >= *exp_date, + LifecycleExpiration::AtDate(exp_date) => { + if let Ok(exp_date) = parse_lifecycle_date(&exp_date) { + now_date >= exp_date + } else { + warn!("Invalid expiraiton date stored in bucket {:?} lifecycle config: {}", bucket.id, exp_date); + false + } + } }; if size_match && date_match {