all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Lukas Wagner <l.wagner@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH datacenter-manager v3 14/21] server: migrate existing ParallelFetcher users to use PdmApplication
Date: Thu, 27 Aug 2026 13:42:37 +0200	[thread overview]
Message-ID: <20260827114244.424784-15-l.wagner@proxmox.com> (raw)
In-Reply-To: <20260827114244.424784-1-l.wagner@proxmox.com>

ParallelFetcher now requires a PdmApplication in both it's `new` and
`builder` constructions. The provided application context object is
later provided via ParallelFetcherArgs<C> to the closure.

For now, this is mostly used to give access to the client factory.

The mandatory argument should gently nudge the developer towards using
the #[state] injected handle, with the option to use context::pdm_application()
as fallback, if too much refactoring is needed on the spot.

Introducing this new mandatory parameter led to a small cascade of
changes in existing users of ParallelFetcher. These were changed to
fully use `app` everywhere they can, such as when reading remote config.
This makes the commit slightly bigger, but avoids any awkward
'in-between' states where only part of a module uses PdmApplication.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---

Notes:
    Changes since v2:
      - Use new #[state] attribute
      - Let ParallelFetcher have PdmApplication, not only
        ClientFactory
      - Merged two previous commits into this one:
    
        https://lore.proxmox.com/pdm-devel/20260820145220.418032-14-l.wagner@proxmox.com/T/#u
        https://lore.proxmox.com/pdm-devel/DKX7UQ5AWO68.3SIX8MRGM180I@proxmox.com/T/#mb0e73f6906c23766b6075751f2056dc86beda8fc

 server/src/api/pve/firewall.rs          | 25 +++++++++-----
 server/src/api/sdn/controllers.rs       | 13 +++++---
 server/src/api/sdn/vnets.rs             | 14 +++++---
 server/src/api/sdn/zones.rs             | 13 +++++---
 server/src/parallel_fetcher.rs          | 44 ++++++++++++++++++++-----
 server/src/remote_tasks/refresh_task.rs |  4 +--
 server/src/remote_updates.rs            |  4 +--
 7 files changed, 83 insertions(+), 34 deletions(-)

diff --git a/server/src/api/pve/firewall.rs b/server/src/api/pve/firewall.rs
index d0f2d6d1..29fb3dce 100644
--- a/server/src/api/pve/firewall.rs
+++ b/server/src/api/pve/firewall.rs
@@ -19,6 +19,7 @@ use pdm_api_types::{PRIV_RESOURCE_AUDIT, PRIV_RESOURCE_MODIFY, PRIV_SYS_MODIFY};
 use super::{connect_to_remote_by_id, find_node_for_vm};
 use crate::api::remotes::RemoteIterator;
 use crate::connection::PveClient;
+use crate::context::PdmApplication;
 use crate::parallel_fetcher::{ParallelFetcher, ParallelFetcherArgs};
 
 // top-level firewall routers
@@ -114,7 +115,10 @@ struct ClusterFirewallData {
 async fn fetch_cluster_firewall_data(
     args: ParallelFetcherArgs<()>,
 ) -> Result<ClusterFirewallData, Error> {
-    let pve = crate::connection::make_pve_client(args.remote())?;
+    let pve = args
+        .pdm_application()
+        .client_factory()
+        .make_pve_client(args.remote())?;
 
     let guests = match pve.cluster_resources(Some(ClusterResourceKind::Vm)).await {
         Ok(guests) => guests,
@@ -204,7 +208,10 @@ async fn load_guests_firewall_status(
 async fn fetch_node_firewall_status(
     args: ParallelFetcherArgs<FirewallFetchContext>,
 ) -> Result<NodeFirewallStatus, Error> {
-    let pve = crate::connection::make_pve_client(args.remote())?;
+    let pve = args
+        .pdm_application()
+        .client_factory()
+        .make_pve_client(args.remote())?;
 
     let options_response = pve.node_firewall_options(args.node());
     let rules_response = pve.list_node_firewall_rules(args.node());
@@ -246,8 +253,9 @@ async fn fetch_node_firewall_status(
 /// Get firewall status of all PVE remotes.
 pub async fn pve_firewall_status(
     _rpcenv: &mut dyn RpcEnvironment,
+    #[state] app: PdmApplication,
 ) -> Result<Vec<RemoteFirewallStatus>, Error> {
-    let pve_remotes: Vec<Remote> = RemoteIterator::new(pdm_config::remotes::instance())?
+    let pve_remotes: Vec<Remote> = RemoteIterator::new(app.remote_config())?
         .remote_type(pdm_api_types::remotes::RemoteType::Pve)
         .into_remotes()
         .collect();
@@ -257,7 +265,7 @@ pub async fn pve_firewall_status(
     }
 
     // 1: fetch cluster-level data (status + guests)
-    let cluster_fetcher = ParallelFetcher::new(());
+    let cluster_fetcher = ParallelFetcher::new(app.clone(), ());
     let cluster_results = cluster_fetcher
         .do_for_all_remotes(pve_remotes.iter().cloned(), fetch_cluster_firewall_data)
         .await;
@@ -275,7 +283,7 @@ pub async fn pve_firewall_status(
         guests: Arc::new(vec![]),
     };
 
-    let node_fetcher = ParallelFetcher::new(context);
+    let node_fetcher = ParallelFetcher::new(app, context);
     let node_results = node_fetcher
         .do_for_all_remote_nodes(pve_remotes.iter().cloned(), move |mut args| {
             if let Some(guests) = guests_per_remote.get(&args.remote().id) {
@@ -352,8 +360,9 @@ pub async fn cluster_firewall_options(
 pub async fn cluster_firewall_status(
     remote: String,
     _rpcenv: &mut dyn RpcEnvironment,
+    #[state] app: PdmApplication,
 ) -> Result<RemoteFirewallStatus, Error> {
-    let (remote_config, _) = pdm_config::remotes::config()?;
+    let (remote_config, _) = app.remote_config().read()?;
 
     let remote_obj = remote_config
         .into_iter()
@@ -362,7 +371,7 @@ pub async fn cluster_firewall_status(
         .ok_or_else(|| anyhow::format_err!("Remote '{}' not found", remote))?;
 
     // 1: fetch cluster-level data (status + guests)
-    let cluster_fetcher = ParallelFetcher::new(());
+    let cluster_fetcher = ParallelFetcher::new(app.clone(), ());
     let cluster_results = cluster_fetcher
         .do_for_all_remotes(
             std::iter::once(remote_obj.clone()),
@@ -390,7 +399,7 @@ pub async fn cluster_firewall_status(
         guests: Arc::new(guests),
     };
 
-    let node_fetcher = ParallelFetcher::new(context);
+    let node_fetcher = ParallelFetcher::new(app, context);
     let node_results = node_fetcher
         .do_for_all_remote_nodes(std::iter::once(remote_obj), fetch_node_firewall_status)
         .await;
diff --git a/server/src/api/sdn/controllers.rs b/server/src/api/sdn/controllers.rs
index 41108674..5400c189 100644
--- a/server/src/api/sdn/controllers.rs
+++ b/server/src/api/sdn/controllers.rs
@@ -9,8 +9,8 @@ use proxmox_router::{Permission, Router, RpcEnvironment, http_bail};
 use proxmox_schema::api;
 use pve_api_types::ListControllersType;
 
-use crate::api::pve;
 use crate::api::remotes::RemoteIterator;
+use crate::context::PdmApplication;
 use crate::parallel_fetcher::ParallelFetcher;
 
 pub const ROUTER: Router = Router::new().get(&API_METHOD_LIST_CONTROLLERS);
@@ -63,6 +63,7 @@ pub async fn list_controllers(
     ty: Option<ListControllersType>,
     remotes: Option<HashSet<String>>,
     rpcenv: &mut dyn RpcEnvironment,
+    #[state] app: PdmApplication,
 ) -> Result<Vec<ListController>, Error> {
     let user_info = CachedUserInfo::new()?;
 
@@ -75,7 +76,7 @@ pub async fn list_controllers(
         http_bail!(FORBIDDEN, "user has no access to resources");
     }
 
-    let mut iter = RemoteIterator::new(pdm_config::remotes::instance())?
+    let mut iter = RemoteIterator::new(app.remote_config())?
         .remote_type(RemoteType::Pve)
         .any_privs(&user_info, &auth_id, PRIV_RESOURCE_AUDIT);
     if let Some(ref filter) = remotes {
@@ -83,11 +84,15 @@ pub async fn list_controllers(
     }
 
     let mut vnets = Vec::new();
-    let fetcher = ParallelFetcher::new((pending, running, ty));
+
+    let fetcher = ParallelFetcher::new(app, (pending, running, ty));
 
     let results = fetcher
         .do_for_all_remotes(iter.into_remotes(), async |args| {
-            Ok(pve::connect(args.remote())?
+            Ok(args
+                .pdm_application()
+                .client_factory()
+                .make_pve_client(args.remote())?
                 .list_controllers(args.context().0, args.context().1, args.context().2)
                 .await?)
         })
diff --git a/server/src/api/sdn/vnets.rs b/server/src/api/sdn/vnets.rs
index 89017438..312d46b6 100644
--- a/server/src/api/sdn/vnets.rs
+++ b/server/src/api/sdn/vnets.rs
@@ -1,6 +1,7 @@
 use std::collections::HashSet;
 
 use anyhow::{Context, Error};
+
 use pbs_api_types::REMOTE_ID_SCHEMA;
 use pdm_api_types::{
     Authid, PRIV_RESOURCE_AUDIT,
@@ -13,8 +14,7 @@ use proxmox_router::{Permission, Router, RpcEnvironment, http_bail};
 use proxmox_schema::api;
 use pve_api_types::{CreateVnet, SdnVnetType};
 
-use crate::api::pve;
-use crate::api::remotes::RemoteIterator;
+use crate::{api::remotes::RemoteIterator, context::PdmApplication};
 use crate::{parallel_fetcher::ParallelFetcher, sdn_client::LockedSdnClients};
 
 pub const ROUTER: Router = Router::new()
@@ -64,6 +64,7 @@ async fn list_vnets(
     running: Option<bool>,
     remotes: Option<HashSet<String>>,
     rpcenv: &mut dyn RpcEnvironment,
+    #[state] app: PdmApplication,
 ) -> Result<Vec<ListVnet>, Error> {
     let user_info = CachedUserInfo::new()?;
 
@@ -76,7 +77,7 @@ async fn list_vnets(
         http_bail!(FORBIDDEN, "user has no access to resources");
     }
 
-    let mut iter = RemoteIterator::new(pdm_config::remotes::instance())?
+    let mut iter = RemoteIterator::new(app.remote_config())?
         .remote_type(RemoteType::Pve)
         .any_privs(&user_info, &auth_id, PRIV_RESOURCE_AUDIT);
     if let Some(ref filter) = remotes {
@@ -84,11 +85,14 @@ async fn list_vnets(
     }
 
     let mut vnets = Vec::new();
-    let fetcher = ParallelFetcher::new((pending, running));
+    let fetcher = ParallelFetcher::new(app, (pending, running));
 
     let results = fetcher
         .do_for_all_remotes(iter.into_remotes(), async |args| {
-            Ok(pve::connect(args.remote())?
+            Ok(args
+                .pdm_application()
+                .client_factory()
+                .make_pve_client(args.remote())?
                 .list_vnets(args.context().0, args.context().1)
                 .await?)
         })
diff --git a/server/src/api/sdn/zones.rs b/server/src/api/sdn/zones.rs
index 5d8d8add..82366e81 100644
--- a/server/src/api/sdn/zones.rs
+++ b/server/src/api/sdn/zones.rs
@@ -14,8 +14,7 @@ use proxmox_router::{Permission, Router, RpcEnvironment, http_bail};
 use proxmox_schema::api;
 use pve_api_types::{CreateZone, ListZonesType};
 
-use crate::api::pve;
-use crate::api::remotes::RemoteIterator;
+use crate::{api::remotes::RemoteIterator, context::PdmApplication};
 use crate::{parallel_fetcher::ParallelFetcher, sdn_client::LockedSdnClients};
 
 pub const ROUTER: Router = Router::new()
@@ -70,6 +69,7 @@ pub async fn list_zones(
     ty: Option<ListZonesType>,
     remotes: Option<HashSet<String>>,
     rpcenv: &mut dyn RpcEnvironment,
+    #[state] app: PdmApplication,
 ) -> Result<Vec<ListZone>, Error> {
     let user_info = CachedUserInfo::new()?;
 
@@ -82,7 +82,7 @@ pub async fn list_zones(
         http_bail!(FORBIDDEN, "user has no access to resources");
     }
 
-    let mut iter = RemoteIterator::new(pdm_config::remotes::instance())?
+    let mut iter = RemoteIterator::new(app.remote_config())?
         .remote_type(RemoteType::Pve)
         .any_privs(&user_info, &auth_id, PRIV_RESOURCE_AUDIT);
     if let Some(ref filter) = remotes {
@@ -90,11 +90,14 @@ pub async fn list_zones(
     }
 
     let mut vnets = Vec::new();
-    let fetcher = ParallelFetcher::new((pending, running, ty));
+    let fetcher = ParallelFetcher::new(app, (pending, running, ty));
 
     let results = fetcher
         .do_for_all_remotes(iter.into_remotes(), async |args| {
-            Ok(pve::connect(args.remote())?
+            Ok(args
+                .pdm_application()
+                .client_factory()
+                .make_pve_client(args.remote())?
                 .list_zones(args.context().0, args.context().1, args.context().2)
                 .await?)
         })
diff --git a/server/src/parallel_fetcher.rs b/server/src/parallel_fetcher.rs
index 512a04d9..b90f02e3 100644
--- a/server/src/parallel_fetcher.rs
+++ b/server/src/parallel_fetcher.rs
@@ -8,6 +8,7 @@
 //! #
 //! # #[tokio::main]
 //! # async fn main() -> Result<(), Error> {
+//! #   let app = server::context::pdm_application();
 //! #   let remotes: Vec<Remote> = Vec::new();
 //! #
 //!     async fn fetch_meaning(
@@ -25,7 +26,7 @@
 //!     // This context can be passed to the function what is executed for every remote node.
 //!     let context = ();
 //!
-//!     let fetcher = ParallelFetcher::builder(context)
+//!     let fetcher = ParallelFetcher::builder(app, context)
 //!         .max_connections(10)
 //!         .max_connections_per_remote(2)
 //!         .build();
@@ -73,7 +74,7 @@ use pve_api_types::ClusterNodeIndexResponse;
 
 use pdm_api_types::remotes::{Remote, RemoteType};
 
-use crate::connection;
+use crate::context::PdmApplication;
 
 /// Maximum number of parallel outgoing API requests.
 pub const DEFAULT_MAX_CONNECTIONS: usize = 20;
@@ -229,15 +230,17 @@ impl<T> NodeResponse<T> {
 pub struct ParallelFetcherBuilder<C> {
     max_connections: Option<usize>,
     max_connections_per_remote: Option<usize>,
+    pdm_application: PdmApplication,
     context: C,
 }
 
 impl<C> ParallelFetcherBuilder<C> {
-    fn new(context: C) -> Self {
+    fn new(pdm_application: PdmApplication, context: C) -> Self {
         Self {
             context,
             max_connections: None,
             max_connections_per_remote: None,
+            pdm_application,
         }
     }
 
@@ -263,6 +266,7 @@ impl<C> ParallelFetcherBuilder<C> {
                 .max_connections_per_remote
                 .unwrap_or(DEFAULT_MAX_CONNECTIONS_PER_REMOTE),
             context: self.context,
+            pdm_application: self.pdm_application,
         }
     }
 }
@@ -280,6 +284,8 @@ pub struct ParallelFetcherArgs<C> {
     /// The node. This may be 'localhost' for PBS remotes or if using
     /// [`ParallelFetcher::do_for_all_remotes`].
     node: String,
+    /// A handle to the [`PdmApplication`] context object.
+    pdm_application: PdmApplication,
 }
 
 impl<C> ParallelFetcherArgs<C> {
@@ -305,24 +311,32 @@ impl<C> ParallelFetcherArgs<C> {
     pub fn context_mut(&mut self) -> &mut C {
         &mut self.context
     }
+
+    /// Get a reference to [`PdmApplication`].
+    ///
+    /// Use this for accessing configuration or constructing API clients.
+    pub fn pdm_application(&self) -> &PdmApplication {
+        &self.pdm_application
+    }
 }
 
 /// Helper for parallelizing API requests to multiple remotes/nodes.
 pub struct ParallelFetcher<C> {
     max_connections: usize,
     max_connections_per_remote: usize,
+    pdm_application: PdmApplication,
     context: C,
 }
 
 impl<C: Clone + Send + 'static> ParallelFetcher<C> {
     /// Create a [`ParallelFetcher`] with default settings.
-    pub fn new(context: C) -> Self {
-        Self::builder(context).build()
+    pub fn new(app: PdmApplication, context: C) -> Self {
+        Self::builder(app, context).build()
     }
 
     /// Create the builder for constructing a [`ParallelFetcher`] with custom settings.
-    pub fn builder(context: C) -> ParallelFetcherBuilder<C> {
-        ParallelFetcherBuilder::new(context)
+    pub fn builder(app: PdmApplication, context: C) -> ParallelFetcherBuilder<C> {
+        ParallelFetcherBuilder::new(app, context)
     }
 
     /// Invoke a function `func` for all nodes of a given list of remotes in parallel.
@@ -344,13 +358,16 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
         for remote in remotes {
             let semaphore = Arc::clone(&total_connections_semaphore);
 
+            let app = self.pdm_application.clone();
             let f = func.clone();
+
             let future = Self::fetch_remote(
                 remote,
                 self.context.clone(),
                 semaphore,
                 f,
                 self.max_connections_per_remote,
+                app,
             );
 
             if let Some(log_context) = LogContext::current() {
@@ -382,6 +399,7 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
         semaphore: Arc<Semaphore>,
         func: F,
         max_connections_per_remote: usize,
+        pdm_application: PdmApplication,
     ) -> RemoteResponse<MultipleNodesResponse<T>>
     where
         F: Fn(ParallelFetcherArgs<C>) -> Ft + Clone + Send + 'static,
@@ -397,8 +415,10 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
             RemoteType::Pve => {
                 let remote_clone = remote.clone();
 
+                let app = &pdm_application;
+
                 let nodes = match async move {
-                    let client = connection::make_pve_client(&remote_clone)?;
+                    let client = app.client_factory().make_pve_client(&remote_clone)?;
                     let nodes = client.list_nodes().await?;
 
                     Ok::<Vec<ClusterNodeIndexResponse>, Error>(nodes)
@@ -433,12 +453,14 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
                     let remote_clone = remote.clone();
                     let node_name = node.node.clone();
                     let context_clone = context.clone();
+                    let app = pdm_application.clone();
 
                     let future = Self::fetch_node(
                         func_clone,
                         context_clone,
                         remote_clone,
                         node_name,
+                        app,
                         permit,
                         Some(per_remote_connections_permit),
                     );
@@ -467,6 +489,7 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
                     context,
                     remote.clone(),
                     "localhost".into(),
+                    pdm_application,
                     permit.unwrap(), // Always set to `Some` at this point
                     None,
                 )
@@ -490,6 +513,7 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
         context: C,
         remote: Remote,
         node: String,
+        pdm_application: PdmApplication,
         _permit: OwnedSemaphorePermit,
         _per_remote_connections_permit: Option<OwnedSemaphorePermit>,
     ) -> NodeResponse<T>
@@ -504,6 +528,7 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
             context,
             remote,
             node: node.clone(),
+            pdm_application,
         };
 
         let result = func(parallel_fetcher_context).await;
@@ -539,7 +564,9 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
             let remote_type = remote.ty;
 
             let context = self.context.clone();
+            let app = self.pdm_application.clone();
             let func = func.clone();
+
             let future = async move {
                 let permit = total_connections_semaphore.acquire_owned().await.unwrap();
 
@@ -551,6 +578,7 @@ impl<C: Clone + Send + 'static> ParallelFetcher<C> {
                         context,
                         remote,
                         "localhost".into(),
+                        app,
                         permit,
                         None,
                     )
diff --git a/server/src/remote_tasks/refresh_task.rs b/server/src/remote_tasks/refresh_task.rs
index c1753114..7d729c22 100644
--- a/server/src/remote_tasks/refresh_task.rs
+++ b/server/src/remote_tasks/refresh_task.rs
@@ -9,7 +9,6 @@ use pdm_api_types::RemoteUpid;
 use pdm_api_types::remotes::{Remote, RemoteType};
 use proxmox_section_config::typed::SectionConfigData;
 
-use crate::api;
 use crate::connection;
 use crate::parallel_fetcher::{ParallelFetcher, ParallelFetcherArgs};
 use crate::pbs_client;
@@ -17,6 +16,7 @@ use crate::remote_tasks::{
     KEEP_OLD_FILES, ROTATE_AFTER,
     task_cache::{GetTasks, NodeFetchSuccessMap, State, TaskCache, TaskCacheItem},
 };
+use crate::{api, context};
 
 /// Interval in seconds at which to fetch the newest tasks from remotes (if there is no tracked
 /// task for this remote).
@@ -234,7 +234,7 @@ async fn fetch_remotes(
     remotes: Vec<Remote>,
     cache_state: Arc<State>,
 ) -> (Vec<TaskCacheItem>, NodeFetchSuccessMap) {
-    let fetcher = ParallelFetcher::builder(cache_state)
+    let fetcher = ParallelFetcher::builder(context::pdm_application(), cache_state)
         .max_connections(MAX_CONNECTIONS)
         .max_connections_per_remote(CONNECTIONS_PER_PVE_REMOTE)
         .build();
diff --git a/server/src/remote_updates.rs b/server/src/remote_updates.rs
index bd648efd..890bbfa9 100644
--- a/server/src/remote_updates.rs
+++ b/server/src/remote_updates.rs
@@ -12,7 +12,7 @@ use pdm_api_types::remotes::{Remote, RemoteType};
 
 use crate::namespaced_cache::CacheError;
 use crate::parallel_fetcher::ParallelFetcher;
-use crate::{api_cache, connection};
+use crate::{api_cache, connection, context};
 
 const OLD_CACHEFILE: &str = concat!(pdm_buildcfg::PDM_CACHE_DIR_M!(), "/remote-updates.json");
 
@@ -229,7 +229,7 @@ async fn update_cached_summary_for_node(
 /// delays its own entry. The final pass records whole-remote failures and prunes vanished
 /// remotes and nodes.
 pub async fn refresh_update_summary_cache(remotes: Vec<Remote>) -> Result<(), Error> {
-    let fetcher = ParallelFetcher::new(());
+    let fetcher = ParallelFetcher::new(context::pdm_application(), ());
 
     let fetch_response = fetcher
         .do_for_all_remote_nodes(remotes.into_iter(), |args| async move {
-- 
2.47.3





  parent reply	other threads:[~2026-08-27 11:43 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-27 11:42 [PATCH datacenter-manager/proxmox v3 00/21] inject application context via API macro for easier integration testing Lukas Wagner
2026-08-27 11:42 ` [PATCH proxmox v3 01/21] router: introduce shared state Lukas Wagner
2026-08-27 11:42 ` [PATCH proxmox v3 02/21] rest-server: allow to inject " Lukas Wagner
2026-08-27 11:42 ` [PATCH proxmox v3 03/21] api-macro: support shared state extraction type Lukas Wagner
2026-08-27 11:42 ` [PATCH proxmox v3 04/21] product-config: add ProductConfig type Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 05/21] context: promote context to a dir-style module Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 06/21] pdm-config: remotes: rename trait methods to read/write/lock Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 07/21] pdm-config: subscriptions: " Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 08/21] remote iterator: pass remote config reader explicitly Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 09/21] context: introduce a ContextFactory to build application context Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 10/21] context: establish PdmApplication object Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 11/21] context: register PdmApplication in router Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 12/21] connection: use client factory from PdmApplication handle Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 13/21] parallel fetcher: pass arguments to closure in a single type Lukas Wagner
2026-08-27 11:42 ` Lukas Wagner [this message]
2026-08-27 11:42 ` [PATCH datacenter-manager v3 15/21] tests: add helpers for building API-handler-level integration tests Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 16/21] tests: add example tests for SDN API routes Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 17/21] api-cache: add wrapper type Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 18/21] context: provide api-cache on the app object Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 19/21] api: subscriptions: use PdmApplication instead of globals Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 20/21] pdm-config: subscriptions: drop unused accessor functions Lukas Wagner
2026-08-27 11:42 ` [PATCH datacenter-manager v3 21/21] tests: add example tests for remote subscription management Lukas Wagner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260827114244.424784-15-l.wagner@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal