From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 853F51FF13F for ; Thu, 29 Jan 2026 14:44:10 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 63FAC5533; Thu, 29 Jan 2026 14:44:33 +0100 (CET) From: Lukas Wagner To: pdm-devel@lists.proxmox.com Subject: [RFC datacenter-manager 1/6] connection: store client factory in an Arc and add public getter Date: Thu, 29 Jan 2026 14:44:13 +0100 Message-ID: <20260129134418.307552-3-l.wagner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260129134418.307552-1-l.wagner@proxmox.com> References: <20260129134418.307552-1-l.wagner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1769694199690 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.037 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: 7WZR5CEFV5NSSQRX3OR6GJCKGIX2NQ4R X-Message-ID-Hash: 7WZR5CEFV5NSSQRX3OR6GJCKGIX2NQ4R X-MailFrom: l.wagner@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 Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This will be useful to truly ensure that everybody is using the same factory. Signed-off-by: Lukas Wagner --- server/src/connection.rs | 29 ++++++++++++------- server/src/context.rs | 4 ++- .../src/metric_collection/collection_task.rs | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/server/src/connection.rs b/server/src/connection.rs index 7e366719..f144f9cf 100644 --- a/server/src/connection.rs +++ b/server/src/connection.rs @@ -26,7 +26,7 @@ use pve_api_types::client::PveClientImpl; use crate::pbs_client::PbsClient; -static INSTANCE: OnceLock> = OnceLock::new(); +static INSTANCE: OnceLock> = OnceLock::new(); /// Connection Info returned from [`prepare_connect_client`] struct ConnectInfo { @@ -396,7 +396,8 @@ impl ClientFactory for DefaultClientFactory { } } -fn instance() -> &'static (dyn ClientFactory + Send + Sync) { +/// Get a handle to the global client factory (as an reference). +pub fn client_factory_ref() -> &'static (dyn ClientFactory + Send + Sync) { // Not initializing the connection factory instance is // entirely in our responsibility and not something we can recover from, // so it should be okay to panic in this case. @@ -406,9 +407,17 @@ fn instance() -> &'static (dyn ClientFactory + Send + Sync) { .as_ref() } +/// Get a handle to the global client factory (as an Arc). +pub fn client_factory() -> Arc { + // Not initializing the connection factory instance is + // entirely in our responsibility and not something we can recover from, + // so it should be okay to panic in this case. + Arc::clone(INSTANCE.get().expect("client factory instance not set")) +} + /// Create a new API client for PVE remotes pub fn make_pve_client(remote: &Remote) -> Result, Error> { - instance().make_pve_client(remote) + client_factory_ref().make_pve_client(remote) } /// Create a new API client for PVE remotes, but for a specific endpoint @@ -416,21 +425,21 @@ pub fn make_pve_client_with_endpoint( remote: &Remote, target_endpoint: Option<&str>, ) -> Result, Error> { - instance().make_pve_client_with_endpoint(remote, target_endpoint) + client_factory_ref().make_pve_client_with_endpoint(remote, target_endpoint) } /// Create a new API client for PVE remotes and try to make it connect to a specific *node*. pub fn make_pve_client_with_node(remote: &Remote, node: &str) -> Result, Error> { - instance().make_pve_client_with_node(remote, node) + client_factory_ref().make_pve_client_with_node(remote, node) } /// Create a new API client for PBS remotes pub fn make_pbs_client(remote: &Remote) -> Result, Error> { - instance().make_pbs_client(remote) + client_factory_ref().make_pbs_client(remote) } pub fn make_raw_client(remote: &Remote) -> Result, Error> { - instance().make_raw_client(remote) + client_factory_ref().make_raw_client(remote) } /// Create a new API client for PVE remotes. @@ -443,7 +452,7 @@ pub fn make_raw_client(remote: &Remote) -> Result, Error> { /// /// Note: currently does not support two factor authentication. pub async fn make_pve_client_and_login(remote: &Remote) -> Result, Error> { - instance().make_pve_client_and_login(remote).await + client_factory_ref().make_pve_client_and_login(remote).await } /// Create a new API client for PBS remotes. @@ -456,13 +465,13 @@ pub async fn make_pve_client_and_login(remote: &Remote) -> Result /// /// Note: currently does not support two factor authentication. pub async fn make_pbs_client_and_login(remote: &Remote) -> Result, Error> { - instance().make_pbs_client_and_login(remote).await + client_factory_ref().make_pbs_client_and_login(remote).await } /// Initialize the [`ClientFactory`] instance. /// /// Will panic if the instance has already been set. -pub fn init(instance: Box) { +pub fn init(instance: Arc) { if INSTANCE.set(instance).is_err() { panic!("connection factory instance already set"); } diff --git a/server/src/context.rs b/server/src/context.rs index c5da0afd..8c841eec 100644 --- a/server/src/context.rs +++ b/server/src/context.rs @@ -2,6 +2,8 @@ //! //! Make sure to call `init` *once* when starting up the API server. +use std::sync::Arc; + use anyhow::Error; use crate::connection; @@ -10,7 +12,7 @@ use crate::connection; #[allow(dead_code)] fn default_remote_setup() { pdm_config::remotes::init(Box::new(pdm_config::remotes::DefaultRemoteConfig)); - connection::init(Box::new(connection::DefaultClientFactory)); + connection::init(Arc::new(connection::DefaultClientFactory)); } /// Dependency-inject concrete implementations needed at runtime. diff --git a/server/src/metric_collection/collection_task.rs b/server/src/metric_collection/collection_task.rs index cc1a460e..00ab2cc0 100644 --- a/server/src/metric_collection/collection_task.rs +++ b/server/src/metric_collection/collection_task.rs @@ -553,7 +553,7 @@ pub(super) mod tests { // TODO: the client factory is currently stored in a OnceLock - // we can only set it from one test... Ideally we'd like to have the // option to set it in every single test if needed - task/thread local? - connection::init(Box::new(TestClientFactory { now })); + connection::init(Arc::new(TestClientFactory { now })); }); now -- 2.47.3