From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 14E5B1FF138 for ; Wed, 04 Mar 2026 14:59:07 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id AB943B1DD; Wed, 4 Mar 2026 15:00:11 +0100 (CET) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup v2 1/2] datastore: s3: modify delete objects api and log exposed errors Date: Wed, 4 Mar 2026 14:59:21 +0100 Message-ID: <20260304135922.717714-6-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260304135922.717714-1-c.ebner@proxmox.com> References: <20260304135922.717714-1-c.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1772632751420 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.053 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: AQ4H7RBZMARIV4WN3E4RST36CUXQ24NH X-Message-ID-Hash: AQ4H7RBZMARIV4WN3E4RST36CUXQ24NH X-MailFrom: c.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Delete objects api calls now return a list of errors in case there were some, not just a boolean flag. Adapt the call sides to the new api interface and log encountered errors. Signed-off-by: Christian Ebner --- changes since version 1: - not present in previous version pbs-datastore/src/backup_info.rs | 8 +++++--- pbs-datastore/src/datastore.rs | 6 ++++-- pbs-datastore/src/s3.rs | 26 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pbs-datastore/src/backup_info.rs b/pbs-datastore/src/backup_info.rs index 859039cf4..55ca9a49e 100644 --- a/pbs-datastore/src/backup_info.rs +++ b/pbs-datastore/src/backup_info.rs @@ -250,14 +250,15 @@ impl BackupGroup { .to_str() .ok_or_else(|| format_err!("invalid group path prefix"))?; let prefix = format!("{S3_CONTENT_PREFIX}/{group_prefix}"); - let delete_objects_error = proxmox_async::runtime::block_on( + let delete_objects_errors = proxmox_async::runtime::block_on( s3_client.delete_objects_by_prefix_with_suffix_filter( &S3PathPrefix::Some(prefix), PROTECTED_MARKER_FILENAME, &[GROUP_OWNER_FILE_NAME, GROUP_NOTES_FILE_NAME], ), )?; - if delete_objects_error { + if !delete_objects_errors.is_empty() { + crate::s3::log_s3_delete_objects_errors(&delete_objects_errors); bail!("deleting objects failed"); } } @@ -636,7 +637,8 @@ impl BackupDir { let delete_objects_error = proxmox_async::runtime::block_on( s3_client.delete_objects_by_prefix(&S3PathPrefix::Some(prefix)), )?; - if delete_objects_error { + if !delete_objects_error.is_empty() { + crate::s3::log_s3_delete_objects_errors(&delete_objects_error); bail!("deleting objects failed"); } } diff --git a/pbs-datastore/src/datastore.rs b/pbs-datastore/src/datastore.rs index 7ad3d917d..ee3da93f7 100644 --- a/pbs-datastore/src/datastore.rs +++ b/pbs-datastore/src/datastore.rs @@ -955,7 +955,8 @@ impl DataStore { &[GROUP_OWNER_FILE_NAME, GROUP_NOTES_FILE_NAME], ), )?; - if delete_objects_error { + if !delete_objects_error.is_empty() { + crate::s3::log_s3_delete_objects_errors(&delete_objects_error); bail!("deleting objects failed"); } } @@ -2403,7 +2404,8 @@ impl DataStore { let prefix = S3PathPrefix::Some(String::default()); let delete_objects_error = proxmox_async::runtime::block_on(s3_client.delete_objects_by_prefix(&prefix))?; - if delete_objects_error { + if !delete_objects_error.is_empty() { + crate::s3::log_s3_delete_objects_errors(&delete_objects_error); bail!("deleting objects failed"); } } diff --git a/pbs-datastore/src/s3.rs b/pbs-datastore/src/s3.rs index 90efe2cb5..985758bb9 100644 --- a/pbs-datastore/src/s3.rs +++ b/pbs-datastore/src/s3.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use anyhow::{bail, format_err, Error}; -use proxmox_s3_client::S3ObjectKey; +use proxmox_s3_client::{DeleteObjectError, S3ObjectKey}; /// Object key prefix to group regular datastore contents (not chunks) pub const S3_CONTENT_PREFIX: &str = ".cnt"; @@ -48,6 +48,30 @@ pub fn object_key_from_digest_with_suffix( S3ObjectKey::try_from(object_key_string.as_str()) } +/// Log errors from delete objects api calls +pub(crate) fn log_s3_delete_objects_errors(errors: &[DeleteObjectError]) { + for error in errors { + log::error!( + "delete object failed: {} {} {}", + error + .key + .as_ref() + .map(|key| key.to_string()) + .unwrap_or_else(|| "None".into()), + error + .code + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_else(|| "None"), + error + .message + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_else(|| "None"), + ); + } +} + #[test] fn test_object_key_from_path() { let path = Path::new("vm/100/2025-07-14T14:20:02Z"); -- 2.47.3