admin api: expose routing rules, cors rules and lifecycle rules

This commit is contained in:
Alex Auvolat
2026-03-06 11:10:30 +01:00
committed by Alex
parent 6c0bb1c9b6
commit 64087172ff
10 changed files with 312 additions and 35 deletions
+1
View File
@@ -44,6 +44,7 @@ url.workspace = true
quick-xml.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
opentelemetry.workspace = true
+9 -4
View File
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use hyper::{header::HeaderName, Method};
@@ -16,7 +17,8 @@ pub struct CorsConfiguration {
pub cors_rules: Vec<CorsRule>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = cors::Rule)]
pub struct CorsRule {
#[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
pub id: Option<Value>,
@@ -32,19 +34,22 @@ pub struct CorsRule {
pub expose_headers: Vec<Value>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = cors::AllowedMethod)]
pub struct AllowedMethod {
#[serde(rename = "AllowedMethod")]
pub allowed_method: Value,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = cors::AllowedHeader)]
pub struct AllowedHeader {
#[serde(rename = "AllowedHeader")]
pub allowed_header: Value,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = cors::ExposedHeader)]
pub struct ExposeHeader {
#[serde(rename = "ExposeHeader")]
pub expose_header: Value,
+12 -4
View File
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use garage_model::bucket_table::{
parse_lifecycle_date, LifecycleExpiration as GarageLifecycleExpiration,
@@ -15,7 +16,8 @@ pub struct LifecycleConfiguration {
pub lifecycle_rules: Vec<LifecycleRule>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[schema(as = lifecycle::Rule)]
pub struct LifecycleRule {
#[serde(rename = "ID", skip_serializing_if = "Option::is_none")]
pub id: Option<Value>,
@@ -37,9 +39,13 @@ pub struct LifecycleRule {
pub abort_incomplete_mpu: Option<AbortIncompleteMpu>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Default)]
#[derive(
Debug, ToSchema, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Default,
)]
#[schema(as = lifecycle::Filter)]
pub struct Filter {
#[serde(rename = "And", skip_serializing_if = "Option::is_none")]
#[schema(no_recursion)]
pub and: Option<Box<Filter>>,
#[serde(rename = "Prefix", skip_serializing_if = "Option::is_none")]
pub prefix: Option<Value>,
@@ -52,7 +58,8 @@ pub struct Filter {
pub size_lt: Option<IntValue>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[schema(as = lifecycle::Expiration)]
pub struct Expiration {
#[serde(rename = "Days", skip_serializing_if = "Option::is_none")]
pub days: Option<IntValue>,
@@ -60,7 +67,8 @@ pub struct Expiration {
pub at_date: Option<Value>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[schema(as = lifecycle::AbortIncompleteMpu)]
pub struct AbortIncompleteMpu {
#[serde(rename = "DaysAfterInitiation")]
pub days: IntValue,
+5 -2
View File
@@ -3,6 +3,7 @@ pub mod lifecycle;
pub mod website;
use serde::{Deserialize, Serialize, Serializer};
use utoipa::ToSchema;
pub fn to_xml_with_header<T: Serialize>(x: &T) -> Result<String, quick_xml::se::SeError> {
use quick_xml::se::{self, EmptyElementHandling, QuoteLevel};
@@ -32,7 +33,8 @@ pub fn xmlns_xsi_tag<S: Serializer>(_v: &(), s: S) -> Result<S::Ok, S::Error> {
s.serialize_str("http://www.w3.org/2001/XMLSchema-instance")
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = xml::Value)]
pub struct Value(#[serde(rename = "$value")] pub String);
impl From<&str> for Value {
@@ -41,5 +43,6 @@ impl From<&str> for Value {
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = xml::IntValue)]
pub struct IntValue(#[serde(rename = "$value")] pub i64);
+30 -7
View File
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use garage_model::bucket_table::{self, WebsiteConfig};
use garage_model::bucket_table::{self, RoutingRule as GarageRoutingRule, WebsiteConfig};
use crate::common_error::CommonError as Error;
use crate::xml::{xmlns_tag, IntValue, Value};
@@ -35,7 +36,8 @@ impl RoutingRules {
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::RoutingRule)]
pub struct RoutingRule {
#[serde(rename = "Condition")]
pub condition: Option<Condition>,
@@ -43,19 +45,22 @@ pub struct RoutingRule {
pub redirect: Redirect,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::Key)]
pub struct Key {
#[serde(rename = "Key")]
pub key: Value,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::Suffix)]
pub struct Suffix {
#[serde(rename = "Suffix")]
pub suffix: Value,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::Target)]
pub struct Target {
#[serde(rename = "HostName")]
pub hostname: Value,
@@ -63,7 +68,8 @@ pub struct Target {
pub protocol: Option<Value>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::Condition)]
pub struct Condition {
#[serde(
rename = "HttpErrorCodeReturnedEquals",
@@ -74,7 +80,8 @@ pub struct Condition {
pub prefix: Option<Value>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, ToSchema, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[schema(as = website::Redirect)]
pub struct Redirect {
#[serde(rename = "HostName", skip_serializing_if = "Option::is_none")]
pub hostname: Option<Value>,
@@ -214,6 +221,22 @@ impl RoutingRule {
}
self.redirect.validate()
}
pub fn from_garage_routing_rule(rule: GarageRoutingRule) -> Self {
RoutingRule {
condition: rule.condition.map(|cond| Condition {
http_error_code: cond.http_error_code.map(|c| IntValue(c as i64)),
prefix: cond.prefix.map(Value),
}),
redirect: Redirect {
hostname: rule.redirect.hostname.map(Value),
http_redirect_code: Some(IntValue(rule.redirect.http_redirect_code as i64)),
protocol: rule.redirect.protocol.map(Value),
replace_full: rule.redirect.replace_key.map(Value),
replace_prefix: rule.redirect.replace_key_prefix.map(Value),
},
}
}
}
impl Condition {