public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Lukas Wagner <l.wagner@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [RFC datacenter-manager 3/6] introduce PdmApplication struct and inject it during API server startup
Date: Thu, 29 Jan 2026 14:44:15 +0100	[thread overview]
Message-ID: <20260129134418.307552-5-l.wagner@proxmox.com> (raw)
In-Reply-To: <20260129134418.307552-1-l.wagner@proxmox.com>

This struct is used as a container for elemental runtime configuration
like base directories and permissions, as well as to aid us in using
dependency injection for things that need to be mocked in tests (e.g.
the client factory).

It is injected into the rest server; a handle to it can be retrieved
from rpcenv that is passed to every API request handler.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 server/src/bin/proxmox-datacenter-api/main.rs |  7 +-
 .../bin/proxmox-datacenter-privileged-api.rs  |  7 +-
 server/src/context.rs                         | 70 +++++++++++++++++++
 3 files changed, 82 insertions(+), 2 deletions(-)

diff --git a/server/src/bin/proxmox-datacenter-api/main.rs b/server/src/bin/proxmox-datacenter-api/main.rs
index 524a4b51..e674e763 100644
--- a/server/src/bin/proxmox-datacenter-api/main.rs
+++ b/server/src/bin/proxmox-datacenter-api/main.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
 use std::net::{IpAddr, Ipv6Addr, SocketAddr};
 use std::path::Path;
 use std::pin::pin;
@@ -28,6 +29,7 @@ use proxmox_auth_api::api::assemble_csrf_prevention_token;
 
 use server::auth;
 use server::auth::csrf::csrf_secret;
+use server::context::PdmApplication;
 use server::metric_collection;
 use server::resource_cache;
 use server::task_utils;
@@ -151,6 +153,8 @@ async fn run(debug: bool) -> Result<(), Error> {
 
     let indexpath = Path::new(pdm_buildcfg::JS_DIR).join("index.hbs");
 
+    let app = Arc::new(PdmApplication::default());
+
     let config = ApiConfig::new(pdm_buildcfg::JS_DIR, RpcEnvironmentType::PUBLIC)
         .privileged_addr(
             std::os::unix::net::SocketAddr::from_pathname(
@@ -183,7 +187,8 @@ async fn run(debug: bool) -> Result<(), Error> {
             Some(dir_opts),
             Some(file_opts),
             &mut command_sock,
-        )?;
+        )?
+        .with_application_context(Arc::clone(&app) as Arc<dyn Any + Send + Sync>);
 
     let rest_server = RestServer::new(config);
     let redirector = proxmox_rest_server::Redirector::new();
diff --git a/server/src/bin/proxmox-datacenter-privileged-api.rs b/server/src/bin/proxmox-datacenter-privileged-api.rs
index 6b490f2b..7e8cf1d2 100644
--- a/server/src/bin/proxmox-datacenter-privileged-api.rs
+++ b/server/src/bin/proxmox-datacenter-privileged-api.rs
@@ -1,5 +1,6 @@
 use std::path::Path;
 use std::pin::pin;
+use std::sync::Arc;
 
 use anyhow::{bail, format_err, Context as _, Error};
 use futures::*;
@@ -15,6 +16,7 @@ use proxmox_router::RpcEnvironmentType;
 use proxmox_sys::fs::CreateOptions;
 
 use server::auth;
+use server::context::PdmApplication;
 
 use pdm_buildcfg::configdir;
 
@@ -131,6 +133,8 @@ async fn run() -> Result<(), Error> {
     let dir_opts = CreateOptions::new().owner(api_user.uid).group(api_user.gid);
     let file_opts = CreateOptions::new().owner(api_user.uid).group(api_user.gid);
 
+    let app = Arc::new(PdmApplication::default());
+
     let config = ApiConfig::new(pdm_buildcfg::JS_DIR, RpcEnvironmentType::PRIVILEGED)
         .auth_handler_func(|h, m| Box::pin(auth::check_auth(h, m)))
         .formatted_router(&["api2"], &server::api::ROUTER)
@@ -145,7 +149,8 @@ async fn run() -> Result<(), Error> {
             Some(dir_opts),
             Some(file_opts),
             &mut command_sock,
-        )?;
+        )?
+        .with_application_context(app);
 
     let rest_server = RestServer::new(config);
     proxmox_rest_server::init_worker_tasks(pdm_buildcfg::PDM_LOG_DIR_M!().into(), file_opts)?;
diff --git a/server/src/context.rs b/server/src/context.rs
index 8c841eec..f9566a58 100644
--- a/server/src/context.rs
+++ b/server/src/context.rs
@@ -2,10 +2,15 @@
 //!
 //! Make sure to call `init` *once* when starting up the API server.
 
+use std::path::{Path, PathBuf};
 use std::sync::Arc;
 
 use anyhow::Error;
 
+use pdm_config::remotes::{DefaultRemoteConfig, RemoteConfig};
+use proxmox_router::RpcEnvironment;
+use proxmox_sys::fs::CreateOptions;
+
 use crate::connection;
 
 /// Dependency-inject production remote-config implementation and remote client factory
@@ -42,3 +47,68 @@ pub fn init() -> Result<(), Error> {
 
     Ok(())
 }
+
+/// Retrieve the [`PdmApplication`] instance from the `rpcenv` handle.
+///
+/// Panics:
+///   - This function will panic if the rpcenv was never initialized with a [`PdmApplication`]
+///   handle.
+pub fn get_application(rpcenv: &dyn RpcEnvironment) -> Arc<PdmApplication> {
+    rpcenv
+        .application_context()
+        .expect("Application context has not been set up")
+        .downcast()
+        .expect("Could not downcast to PdmApplication")
+}
+
+pub struct PdmApplication {
+    // TODO: These should not be public, but only be accessed through getters.
+    // TODO: Consider a builder to construct this struct.
+    pub remote_config: Arc<dyn RemoteConfig + Send + Sync>,
+    pub client_factory: Arc<dyn connection::ClientFactory + Send + Sync>,
+
+    pub config_path: PathBuf,
+    pub cache_path: PathBuf,
+
+    pub default_create_options: CreateOptions,
+}
+
+impl PdmApplication {
+    // FIXME: What kind of methods should be here or in 'sub-objects' needs to be
+    // determined. What we don't want is some universal 'god object' that kind of
+    // does everything at once.
+
+    pub fn remote_config(&self) -> Arc<dyn RemoteConfig + Send + Sync> {
+        Arc::clone(&self.remote_config)
+    }
+
+    pub fn client_factory(&self) -> Arc<dyn connection::ClientFactory + Send + Sync> {
+        Arc::clone(&self.client_factory)
+    }
+
+    pub fn config_path(&self) -> &Path {
+        &self.config_path
+    }
+
+    pub fn cache_path(&self) -> &Path {
+        &self.cache_path
+    }
+
+    pub fn default_create_options(&self) -> CreateOptions {
+        self.default_create_options
+    }
+}
+
+impl Default for PdmApplication {
+    /// Only for convenience for now.
+    fn default() -> Self {
+        Self {
+            remote_config: Arc::new(DefaultRemoteConfig),
+            client_factory: connection::client_factory(),
+            config_path: pdm_buildcfg::CONFIGDIR.into(),
+            cache_path: pdm_buildcfg::PDM_CACHE_DIR.into(),
+            // TODO: These should come from somewhere else.
+            default_create_options: proxmox_product_config::default_create_options(),
+        }
+    }
+}
-- 
2.47.3





  parent reply	other threads:[~2026-01-29 13:44 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-29 13:44 [RFC datacenter-manager/proxmox 0/7] inject application context via rpcenv for easier integration testing Lukas Wagner
2026-01-29 13:44 ` [RFC proxmox 1/1] router: rpc environment: allow to provide a application-specific context handle via rpcenv Lukas Wagner
2026-01-29 13:44 ` [RFC datacenter-manager 1/6] connection: store client factory in an Arc and add public getter Lukas Wagner
2026-01-29 13:44 ` [RFC datacenter-manager 2/6] parallel fetcher: allow to use custom client factory Lukas Wagner
2026-01-29 13:44 ` Lukas Wagner [this message]
2026-01-29 13:44 ` [RFC datacenter-manager 4/6] remote updates: use PdmApplication object to derive paths, permissions and " Lukas Wagner
2026-01-29 13:44 ` [RFC datacenter-manager 5/6] tests: add captured responses for integration tests Lukas Wagner
2026-01-29 13:44 ` [RFC datacenter-manager 6/6] tests: add basic integration tests for the remote updates API Lukas Wagner
2026-02-03 11:02 ` [RFC datacenter-manager/proxmox 0/7] inject application context via rpcenv for easier integration testing Robert Obkircher

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=20260129134418.307552-5-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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal