cli_v2: migrate cleanupincompleteuploads to Admin API

This commit is contained in:
Alex 2025-01-30 17:45:54 +01:00
parent 3192088aac
commit bb5e0c210f
6 changed files with 72 additions and 66 deletions

View file

@ -61,6 +61,7 @@ admin_endpoints![
CreateBucket, CreateBucket,
UpdateBucket, UpdateBucket,
DeleteBucket, DeleteBucket,
CleanupIncompleteUploads,
// Operations on permissions for keys on buckets // Operations on permissions for keys on buckets
AllowBucketKey, AllowBucketKey,
@ -496,6 +497,19 @@ pub struct DeleteBucketRequest {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteBucketResponse; pub struct DeleteBucketResponse;
// ---- CleanupIncompleteUploads ----
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CleanupIncompleteUploadsRequest {
pub bucket_id: String,
pub older_than_secs: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CleanupIncompleteUploadsResponse {
pub uploads_deleted: u64,
}
// ********************************************** // **********************************************
// Operations on permissions for keys on buckets // Operations on permissions for keys on buckets
// ********************************************** // **********************************************

View file

@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
@ -387,6 +388,26 @@ impl EndpointHandler for UpdateBucketRequest {
} }
} }
#[async_trait]
impl EndpointHandler for CleanupIncompleteUploadsRequest {
type Response = CleanupIncompleteUploadsResponse;
async fn handle(self, garage: &Arc<Garage>) -> Result<CleanupIncompleteUploadsResponse, Error> {
let duration = Duration::from_secs(self.older_than_secs);
let bucket_id = parse_bucket_id(&self.bucket_id)?;
let count = garage
.bucket_helper()
.cleanup_incomplete_uploads(&bucket_id, duration)
.await?;
Ok(CleanupIncompleteUploadsResponse {
uploads_deleted: count as u64,
})
}
}
// ---- BUCKET/KEY PERMISSIONS ---- // ---- BUCKET/KEY PERMISSIONS ----
#[async_trait] #[async_trait]

View file

@ -51,6 +51,7 @@ impl AdminApiRequest {
POST CreateBucket (body), POST CreateBucket (body),
POST DeleteBucket (query::id), POST DeleteBucket (query::id),
POST UpdateBucket (body_field, query::id), POST UpdateBucket (body_field, query::id),
POST CleanupIncompleteUploads (body),
// Bucket-key permissions // Bucket-key permissions
POST AllowBucketKey (body), POST AllowBucketKey (body),
POST DenyBucketKey (body), POST DenyBucketKey (body),

View file

@ -1,53 +0,0 @@
use std::fmt::Write;
use garage_model::helper::error::{Error, OkOrBadRequest};
use crate::cli::*;
use super::*;
impl AdminRpcHandler {
pub(super) async fn handle_bucket_cmd(&self, cmd: &BucketOperation) -> Result<AdminRpc, Error> {
match cmd {
BucketOperation::CleanupIncompleteUploads(query) => {
self.handle_bucket_cleanup_incomplete_uploads(query).await
}
_ => unreachable!(),
}
}
async fn handle_bucket_cleanup_incomplete_uploads(
&self,
query: &CleanupIncompleteUploadsOpt,
) -> Result<AdminRpc, Error> {
let mut bucket_ids = vec![];
for b in query.buckets.iter() {
bucket_ids.push(
self.garage
.bucket_helper()
.admin_get_existing_matching_bucket(b)
.await?,
);
}
let duration = parse_duration::parse::parse(&query.older_than)
.ok_or_bad_request("Invalid duration passed for --older-than parameter")?;
let mut ret = String::new();
for bucket in bucket_ids {
let count = self
.garage
.bucket_helper()
.cleanup_incomplete_uploads(&bucket, duration)
.await?;
writeln!(
&mut ret,
"Bucket {:?}: {} incomplete uploads aborted",
bucket, count
)
.unwrap();
}
Ok(AdminRpc::Ok(ret))
}
}

View file

@ -1,5 +1,4 @@
mod block; mod block;
mod bucket;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@ -39,7 +38,6 @@ pub const ADMIN_RPC_PATH: &str = "garage/admin_rpc.rs/Rpc";
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
pub enum AdminRpc { pub enum AdminRpc {
BucketOperation(BucketOperation),
LaunchRepair(RepairOpt), LaunchRepair(RepairOpt),
Stats(StatsOpt), Stats(StatsOpt),
Worker(WorkerOperation), Worker(WorkerOperation),
@ -532,7 +530,6 @@ impl EndpointHandler<AdminRpc> for AdminRpcHandler {
_from: NodeID, _from: NodeID,
) -> Result<AdminRpc, Error> { ) -> Result<AdminRpc, Error> {
match message { match message {
AdminRpc::BucketOperation(bo) => self.handle_bucket_cmd(bo).await,
AdminRpc::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await, AdminRpc::LaunchRepair(opt) => self.handle_launch_repair(opt.clone()).await,
AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await, AdminRpc::Stats(opt) => self.handle_stats(opt.clone()).await,
AdminRpc::Worker(wo) => self.handle_worker_cmd(wo).await, AdminRpc::Worker(wo) => self.handle_worker_cmd(wo).await,

View file

@ -5,7 +5,6 @@ use garage_util::error::*;
use garage_api::admin::api::*; use garage_api::admin::api::*;
use crate::cli as cli_v1;
use crate::cli::structs::*; use crate::cli::structs::*;
use crate::cli_v2::*; use crate::cli_v2::*;
@ -22,15 +21,9 @@ impl Cli {
BucketOperation::Deny(query) => self.cmd_bucket_deny(query).await, BucketOperation::Deny(query) => self.cmd_bucket_deny(query).await,
BucketOperation::Website(query) => self.cmd_bucket_website(query).await, BucketOperation::Website(query) => self.cmd_bucket_website(query).await,
BucketOperation::SetQuotas(query) => self.cmd_bucket_set_quotas(query).await, BucketOperation::SetQuotas(query) => self.cmd_bucket_set_quotas(query).await,
BucketOperation::CleanupIncompleteUploads(query) => {
// TODO self.cmd_cleanup_incomplete_uploads(query).await
x => cli_v1::cmd_admin( }
&self.admin_rpc_endpoint,
self.rpc_host,
AdminRpc::BucketOperation(x),
)
.await
.ok_or_message("old error"),
} }
} }
@ -520,4 +513,37 @@ impl Cli {
Ok(()) Ok(())
} }
pub async fn cmd_cleanup_incomplete_uploads(
&self,
opt: CleanupIncompleteUploadsOpt,
) -> Result<(), Error> {
let older_than = parse_duration::parse::parse(&opt.older_than)
.ok_or_message("Invalid duration passed for --older-than parameter")?;
for b in opt.buckets.iter() {
let bucket = self
.api_request(GetBucketInfoRequest {
id: None,
global_alias: None,
search: Some(b.clone()),
})
.await?;
let res = self
.api_request(CleanupIncompleteUploadsRequest {
bucket_id: bucket.id.clone(),
older_than_secs: older_than.as_secs(),
})
.await?;
if res.uploads_deleted > 0 {
println!("{:.16}: {} uploads deleted", bucket.id, res.uploads_deleted);
} else {
println!("{:.16}: no uploads deleted", bucket.id);
}
}
Ok(())
}
} }