From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pbs-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 831691FF170 for <inbox@lore.proxmox.com>; Thu, 29 May 2025 16:32:29 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B089A15C74; Thu, 29 May 2025 16:32:33 +0200 (CEST) From: Christian Ebner <c.ebner@proxmox.com> To: pbs-devel@lists.proxmox.com Date: Thu, 29 May 2025 16:31:41 +0200 Message-Id: <20250529143207.694497-17-c.ebner@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250529143207.694497-1-c.ebner@proxmox.com> References: <20250529143207.694497-1-c.ebner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.033 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 Subject: [pbs-devel] [RFC v2 proxmox-backup 16/42] api/bin: add endpoint and command to check s3 client connection X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion <pbs-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/> List-Post: <mailto:pbs-devel@lists.proxmox.com> List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox Backup Server development discussion <pbs-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" <pbs-devel-bounces@lists.proxmox.com> Adds a dedicated api endpoint and a proxmox-backup-manager command to check if the configured S3 client can reach the bucket. Signed-off-by: Christian Ebner <c.ebner@proxmox.com> --- src/api2/admin/mod.rs | 2 + src/api2/admin/s3.rs | 72 +++++++++++++++++++++++++++ src/bin/proxmox-backup-manager.rs | 1 + src/bin/proxmox_backup_manager/mod.rs | 2 + src/bin/proxmox_backup_manager/s3.rs | 34 +++++++++++++ 5 files changed, 111 insertions(+) create mode 100644 src/api2/admin/s3.rs create mode 100644 src/bin/proxmox_backup_manager/s3.rs diff --git a/src/api2/admin/mod.rs b/src/api2/admin/mod.rs index a1c49f8e2..7694de4b9 100644 --- a/src/api2/admin/mod.rs +++ b/src/api2/admin/mod.rs @@ -9,6 +9,7 @@ pub mod gc; pub mod metrics; pub mod namespace; pub mod prune; +pub mod s3; pub mod sync; pub mod traffic_control; pub mod verify; @@ -19,6 +20,7 @@ const SUBDIRS: SubdirMap = &sorted!([ ("metrics", &metrics::ROUTER), ("prune", &prune::ROUTER), ("gc", &gc::ROUTER), + ("s3", &s3::ROUTER), ("sync", &sync::ROUTER), ("traffic-control", &traffic_control::ROUTER), ("verify", &verify::ROUTER), diff --git a/src/api2/admin/s3.rs b/src/api2/admin/s3.rs new file mode 100644 index 000000000..229bcc535 --- /dev/null +++ b/src/api2/admin/s3.rs @@ -0,0 +1,72 @@ +//! S3 bucket operations + +use anyhow::{Context, Error}; +use hyper::Body; +use serde_json::Value; + +use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap}; +use proxmox_schema::*; +use proxmox_sortable_macro::sortable; + +use pbs_api_types::{S3ClientConfig, S3ClientSecretsConfig, PRIV_SYS_MODIFY, S3_CLIENT_ID_SCHEMA}; + +#[api( + input: { + properties: { + "s3-client-id": { + schema: S3_CLIENT_ID_SCHEMA , + }, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Perform basic sanity check for given s3 client configuration +pub async fn check(s3_client_id: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> { + let (config, _digest) = pbs_config::s3::config()?; + let config: S3ClientConfig = config + .lookup("s3client", &s3_client_id) + .context("config lookup failed")?; + let (secrets, _secrets_digest) = pbs_config::s3::secrets_config()?; + let secrets: S3ClientSecretsConfig = secrets + .lookup("s3secrets", &s3_client_id) + .context("secrets lookup failed")?; + + let options = pbs_s3_client::S3ClientOptions { + host: config.host, + port: config.port, + bucket: config.bucket, + region: config.region.unwrap_or_default(), + fingerprint: config.fingerprint, + access_key: config.access_key, + secret_key: secrets.secret_key, + }; + + let test_object_key = ".s3-client-test"; + let client = pbs_s3_client::S3Client::new(options).context("client creation failed")?; + client.head_bucket().await.context("head object failed")?; + client + .put_object(test_object_key.into(), Body::empty()) + .await + .context("put object failed")?; + client + .get_object(test_object_key.into()) + .await + .context("get object failed")?; + client + .delete_object(test_object_key.into()) + .await + .context("delete object failed")?; + + Ok(Value::Null) +} + +#[sortable] +const S3_OPERATION_SUBDIRS: SubdirMap = &[("check", &Router::new().get(&API_METHOD_CHECK))]; + +const S3_OPERATION_ROUTER: Router = Router::new() + .get(&list_subdirs_api_method!(S3_OPERATION_SUBDIRS)) + .subdirs(S3_OPERATION_SUBDIRS); + +pub const ROUTER: Router = Router::new().match_all("s3-client-id", &S3_OPERATION_ROUTER); diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index d4363e717..68d87c676 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -677,6 +677,7 @@ async fn run() -> Result<(), Error> { .insert("garbage-collection", garbage_collection_commands()) .insert("acme", acme_mgmt_cli()) .insert("cert", cert_mgmt_cli()) + .insert("s3", s3_commands()) .insert("subscription", subscription_commands()) .insert("sync-job", sync_job_commands()) .insert("verify-job", verify_job_commands()) diff --git a/src/bin/proxmox_backup_manager/mod.rs b/src/bin/proxmox_backup_manager/mod.rs index 9b5c73e9a..312a6db6b 100644 --- a/src/bin/proxmox_backup_manager/mod.rs +++ b/src/bin/proxmox_backup_manager/mod.rs @@ -26,6 +26,8 @@ mod prune; pub use prune::*; mod remote; pub use remote::*; +mod s3; +pub use s3::*; mod subscription; pub use subscription::*; mod sync; diff --git a/src/bin/proxmox_backup_manager/s3.rs b/src/bin/proxmox_backup_manager/s3.rs new file mode 100644 index 000000000..a92d3d1b2 --- /dev/null +++ b/src/bin/proxmox_backup_manager/s3.rs @@ -0,0 +1,34 @@ +use pbs_api_types::S3_CLIENT_ID_SCHEMA; +use proxmox_router::{cli::*, RpcEnvironment}; +use proxmox_schema::api; + +use proxmox_backup::api2; + +use anyhow::Error; +use serde_json::Value; + +#[api( + input: { + properties: { + "s3-client-id": { + schema: S3_CLIENT_ID_SCHEMA, + }, + }, + }, +)] +/// Perform basic sanity checks for given S3 client configuration +async fn check(s3_client_id: String, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> { + api2::admin::s3::check(s3_client_id, rpcenv).await?; + Ok(Value::Null) +} + +pub fn s3_commands() -> CommandLineInterface { + let cmd_def = CliCommandMap::new().insert( + "check", + CliCommand::new(&API_METHOD_CHECK) + .arg_param(&["s3-client-id"]) + .completion_cb("s3-client-id", pbs_config::s3::complete_s3_client_id), + ); + + cmd_def.into() +} -- 2.39.5 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel