From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 4B4BE1FF09B for ; Mon, 17 Aug 2026 14:58:15 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id CBBEA217C0; Mon, 17 Aug 2026 14:57:58 +0200 (CEST) From: Lukas Wagner To: pdm-devel@lists.proxmox.com Subject: [PATCH datacenter-manager 17/20] context: provide api-cache on the app object Date: Mon, 17 Aug 2026 14:57:24 +0200 Message-ID: <20260817125727.454039-18-l.wagner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260817125727.454039-1-l.wagner@proxmox.com> References: <20260817125727.454039-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: 1786971437184 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.872 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust 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: F5RTK5DHIP4GSG44MPS3QOHSI5OXZCL3 X-Message-ID-Hash: F5RTK5DHIP4GSG44MPS3QOHSI5OXZCL3 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: Move the ApiCache instance from the global CACHE static onto PdmApplication, built from the product config's cache directory instead of the hardcoded PDM_API_CACHE_PATH. The free functions in api_cache now fetch it via context::pdm_application() instead of the static. Add default_file_create_options()/default_dir_create_options() to ProductConfig so the cache's directory and file permissions can be derived the same way in both the real application and tests. Signed-off-by: Lukas Wagner --- server/src/api_cache.rs | 45 ++++++++++--------- .../bin/proxmox-datacenter-privileged-api.rs | 7 ++- server/src/context/mod.rs | 20 ++++++++- server/src/context/product_config.rs | 23 +++++++++- server/tests/common/test_application.rs | 3 ++ 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/server/src/api_cache.rs b/server/src/api_cache.rs index 6dc8a043..fed66483 100644 --- a/server/src/api_cache.rs +++ b/server/src/api_cache.rs @@ -54,31 +54,22 @@ //! ``` use std::path::PathBuf; -use std::sync::LazyLock; use std::time::Duration; -use nix::sys::stat::Mode; - use proxmox_sys::fs::CreateOptions; +use crate::context; use crate::namespaced_cache::{ BlockingReadableCacheNamespace, BlockingWritableCacheNamespace, CacheError, NamespacedCache, ReadableCacheNamespace, WritableCacheNamespace, }; -/// Path at which API responses are cached. -pub const PDM_API_CACHE_PATH: &str = concat!(pdm_buildcfg::PDM_RUN_DIR_M!(), "/api-cache"); +/// Subdirectory at which API responses are cached. +pub const PDM_API_CACHE_SUBDIR: &str = "api-cache"; const GLOBAL_NAMESPACE: &str = "global"; const LOCK_TIMEOUT: Duration = Duration::from_secs(10); -static CACHE: LazyLock = LazyLock::new(|| { - let file_options = proxmox_product_config::default_create_options(); - let dir_options = file_options.perm(Mode::from_bits_truncate(0o750)); - - ApiCache::new(PathBuf::from(PDM_API_CACHE_PATH), dir_options, file_options) -}); - fn format_remote_namespace(remote: &str) -> String { format!("remote-{remote}") } @@ -157,40 +148,54 @@ impl ApiCache { /// Lock the cache for reading remote-specific data (blocking interface). pub fn read_remote_blocking(remote: &str) -> Result { - CACHE.read_remote_blocking(remote) + context::pdm_application() + .api_cache() + .read_remote_blocking(remote) } /// Lock the cache for writing remote-specific data (blocking interface). pub fn write_remote_blocking(remote: &str) -> Result { - CACHE.write_remote_blocking(remote) + context::pdm_application() + .api_cache() + .write_remote_blocking(remote) } /// Lock the cache for reading global data (blocking interface). pub fn read_global_blocking() -> Result { - CACHE.read_global_blocking() + context::pdm_application() + .api_cache() + .read_global_blocking() } /// Lock the cache for writing global data (blocking interface). pub fn write_global_blocking() -> Result { - CACHE.write_global_blocking() + context::pdm_application() + .api_cache() + .write_global_blocking() } /// Lock the cache for reading remote-specific data (async interface). pub async fn read_remote(remote: &str) -> Result { - CACHE.read_remote(remote).await + context::pdm_application() + .api_cache() + .read_remote(remote) + .await } /// Lock the cache for writing remote-specific data (async interface). pub async fn write_remote(remote: &str) -> Result { - CACHE.write_remote(remote).await + context::pdm_application() + .api_cache() + .write_remote(remote) + .await } /// Lock the cache for reading global data (async interface). pub async fn read_global() -> Result { - CACHE.read_global().await + context::pdm_application().api_cache().read_global().await } /// Lock the cache for writing global data (async interface). pub async fn write_global() -> Result { - CACHE.write_global().await + context::pdm_application().api_cache().write_global().await } diff --git a/server/src/bin/proxmox-datacenter-privileged-api.rs b/server/src/bin/proxmox-datacenter-privileged-api.rs index 3815ab03..d32e10c5 100644 --- a/server/src/bin/proxmox-datacenter-privileged-api.rs +++ b/server/src/bin/proxmox-datacenter-privileged-api.rs @@ -2,6 +2,7 @@ use std::path::Path; use std::pin::pin; use anyhow::{Context as _, Error, bail, format_err}; +use const_format::concatcp; use futures::*; use hyper_util::server::graceful::GracefulShutdown; use nix::fcntl::AtFlags; @@ -104,7 +105,11 @@ fn create_directories() -> Result<(), Error> { )?; pdm_config::setup::mkdir_perms( - api_cache::PDM_API_CACHE_PATH, + concatcp!( + pdm_buildcfg::PDM_RUN_DIR_M!(), + "/", + api_cache::PDM_API_CACHE_SUBDIR, + ), api_user.uid, api_user.gid, 0o750, diff --git a/server/src/context/mod.rs b/server/src/context/mod.rs index bf9c6c2e..5f95d358 100644 --- a/server/src/context/mod.rs +++ b/server/src/context/mod.rs @@ -8,6 +8,7 @@ use anyhow::Error; use pdm_config::{remotes::RemoteConfig, subscriptions::SubscriptionKeyConfig}; +use crate::api_cache::ApiCache; use crate::connection::{self, ClientFactory}; #[cfg(remote_config = "faked")] @@ -80,12 +81,22 @@ pub trait ContextFactory { } fn make_pdm_application(&self) -> Result { + let product_config = self.make_product_config()?; + + let api_cache = ApiCache::new( + product_config.run_dir().join("api-cache"), + product_config.default_dir_create_options(), + product_config.default_file_create_options(), + ); + Ok(PdmApplication { inner: Arc::new(PdmApplicationInner { client_factory: self.make_client_factory()?, remote_config: self.make_remote_config()?, subscription_key_config: self.make_subscription_key_config()?, - product_config: self.make_product_config()?, + product_config, + + api_cache, }), }) } @@ -140,6 +151,11 @@ impl PdmApplication { pub fn product_config(&self) -> &ProductConfig { &self.inner.product_config } + + /// Get a reference to the [`ApiCache`]. + pub fn api_cache(&self) -> &ApiCache { + &self.inner.api_cache + } } struct PdmApplicationInner { @@ -147,4 +163,6 @@ struct PdmApplicationInner { remote_config: Box, subscription_key_config: Box, product_config: ProductConfig, + + api_cache: ApiCache, } diff --git a/server/src/context/product_config.rs b/server/src/context/product_config.rs index 3c1f44ab..a11c5db1 100644 --- a/server/src/context/product_config.rs +++ b/server/src/context/product_config.rs @@ -3,7 +3,8 @@ use std::path::{Path, PathBuf}; use anyhow::Error; -use nix::unistd::User; +use nix::{sys::stat::Mode, unistd::User}; +use proxmox_sys::fs::CreateOptions; #[derive(Clone, Debug)] pub struct ProductConfig { @@ -43,6 +44,26 @@ impl ProductConfig { pub fn cache_dir(&self) -> &Path { &self.cache_dir } + + pub fn default_file_create_options(&self) -> CreateOptions { + let api_user = self.api_user(); + let mode = Mode::from_bits_truncate(0o0640); + + CreateOptions::new() + .perm(mode) + .owner(api_user.uid) + .group(api_user.gid) + } + + pub fn default_dir_create_options(&self) -> CreateOptions { + let api_user = self.api_user(); + let mode = Mode::from_bits_truncate(0o0750); + + CreateOptions::new() + .perm(mode) + .owner(api_user.uid) + .group(api_user.gid) + } } #[derive(Default)] diff --git a/server/tests/common/test_application.rs b/server/tests/common/test_application.rs index cd8409e3..436513f0 100644 --- a/server/tests/common/test_application.rs +++ b/server/tests/common/test_application.rs @@ -142,6 +142,9 @@ impl ContextFactory for TestApplication { std::fs::create_dir(&run_dir)?; std::fs::create_dir(&cache_dir)?; + // FIXME: Maybe ApiCache::new should do this. + std::fs::create_dir(run_dir.join("api-cache"))?; + ProductConfig::builder() .api_user(user.clone()) .priv_user(user) -- 2.47.3