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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox