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: [PATCH datacenter-manager/proxmox v3 00/21] inject application context via API macro for easier integration testing
Date: Thu, 27 Aug 2026 13:42:23 +0200	[thread overview]
Message-ID: <20260827114244.424784-1-l.wagner@proxmox.com> (raw)

TL;DR: Inject essential runtime config, client factory, etc. as an application
context object and allow to retrieve this object easily in an API handler via
the API macro. This allows us directly call API handler implementations from
integration tests. The aim is to make it easier to write good tests on a larger
level.

## Rationale

Considering the different stages of automated software testing, unit testing
(test small software components in isolation) integration testing (test
multiple components together, specifically their interactions) and
end-to-end testing (test the entire application in a context as close to
production as possible), I've found that the middle one, integration testing is
a very challenging one in our stack.

I've found that the challenges with integration testing mostly stem from the
following:

   - "hardcoded" (as in, determined by some constant or literally
     hard-coded) assumptions about storage paths (config, state, caches) and
     users/permissions. This makes it challenging to call into the component
     from a test running as normal user, e.g. from a regular `cargo test`.
 
   - use of global/static instances (examples: Worker task context,
    proxmox-product-config, client factory in PDM...) in application code and
    shared crates. While this can be okay for things that are truly global (e.g.
    logging), it often hinders testing and  especially test isolation due to hidden
    dependencies between test cases and some potential internal state of the global
    instance. This is one of the common causes of flaky tests. Also, the setup of
    these global instances in test cases is always a bit awkward, since the order
    of test execution is not defined (and might actually run in parallel), so some
    kind of synchronization between the test cases is necessary. Furthermore,
    certain test cases might require a *different* setup for these global instances
    (example: client factory that should produce a different kind of mocked PVE
    client); but since we usually put these in OnceLocks, one has to put these
    tests into a separate test binary.

  - tight coupling between major subsystems (e.g. one part calling into the
    other without any clear boundary or abstraction via callbacks or traits)


With regards to PDM, there are a couple of things that need considering when testing:
  - Filesystem access to config, state and caches
  - Interactions with remotes via their API

I feel like if we find a sensible way to abstract these for tests, we can
reasonably cover a huge amount of the code in the backend.

## Implementation

The core idea is to provide a context/application object ('struct
PdmApplication') and "injecting" it into API handlers via special `State`
parameters, automatically handled by the API macro.

This new context object would give access to:
  - base paths for caches, config, state
  - file permissions, user/group
  - client factory
  - api cache

The PdmApplication object is set up during daemon startup, stored in the RpcEnvironment,
and then cloned for each API request, assuming that the handle requested access via
an State<PdmApplication> parameter.

PdmApplication is a thin wrapper around Arc<PdmApplicationInner>, which stores the actual
configuration and trait objects.

This general pattern of injecting application state/context via a parameter
to the handler is something that is also common in other Rust-based
web frameworks, e.g. [actix-web] and [axum].

## References:

[actix-web]: https://actix.rs/docs/application/#state
[axum]: https://docs.rs/axum/latest/axum/#sharing-state-with-handlers


## Bumps needed

 - proxmox-router
 - proxmox-rest-server
 - proxmox-api-macro

## Changes since v1:
  - fix a couple typos, grammar mistakes
  - extend the commit message for the commit that adds the type-keyed map
    to RpcEnvironment
  - fix test case for API macro
  - consistently use app handle in node subscpription api handler
  - in read_captured_response, don't unwrap but return an Error
  - add missing doc comments

## Changes since v2:
  - Mark state arguments using an attribute, not via a wrapper type
    This now supports the following ways to use injected types

    #[state] foo: Foo
    #[state] foo: &Foo
    #[state] foo: Option<Foo>
    #[state] foo: Option<&Foo>

    The optional ones are None if the requested type was never registered.
    In this series we don't use it yet, but I think this is still a
    valuabe addition, which is why I included it for the review.

    The reference variants borrow the type from the registry.

    Using an attribute instead of a wrapper type has the advantage that
    it makes it less likely to have a collision due to type names;
    State<T> was too generic to rule out conflicts

  - Move ProductConfig to proxmox-product-config.
    Also add documentation and further helpers for building CreateOptions

  - Give more context on why the ApiCache wrapper type is added
  - Use CARGO_TARGET_TMPDIR instead of /tmp
  - ParallelFetcher now provides a PdmApplication handle via its args type,
    mainly for future proofing. PdmApplication is now also a mandatory
    argument when building a ParallelFetcher, this gently nudges
    developers to use #[state] PdmApplication at the API level
  - In the firewall API module, consistently use the app context object


proxmox:

Lukas Wagner (4):
  router: introduce shared state
  rest-server: allow to inject shared state
  api-macro: support shared state extraction type
  product-config: add ProductConfig type

 proxmox-api-macro/src/api/method.rs          | 228 +++++++++++++++++-
 proxmox-api-macro/src/api/mod.rs             |   6 +
 proxmox-api-macro/src/lib.rs                 |  56 ++++-
 proxmox-api-macro/tests/state.rs             | 240 +++++++++++++++++++
 proxmox-product-config/src/lib.rs            |   3 +
 proxmox-product-config/src/product_config.rs | 205 ++++++++++++++++
 proxmox-rest-server/src/api_config.rs        |  14 +-
 proxmox-rest-server/src/environment.rs       |   6 +-
 proxmox-router/src/cli/environment.rs        |  12 +-
 proxmox-router/src/lib.rs                    |   2 +
 proxmox-router/src/rpc_environment.rs        |   7 +
 proxmox-router/src/shared_state.rs           |  46 ++++
 12 files changed, 812 insertions(+), 13 deletions(-)
 create mode 100644 proxmox-api-macro/tests/state.rs
 create mode 100644 proxmox-product-config/src/product_config.rs
 create mode 100644 proxmox-router/src/shared_state.rs


proxmox-datacenter-manager:

Lukas Wagner (17):
  context: promote context to a dir-style module
  pdm-config: remotes: rename trait methods to read/write/lock
  pdm-config: subscriptions: rename trait methods to read/write/lock
  remote iterator: pass remote config reader explicitly
  context: introduce a ContextFactory to build application context
  context: establish PdmApplication object
  context: register PdmApplication in router
  connection: use client factory from PdmApplication handle
  parallel fetcher: pass arguments to closure in a single type
  server: migrate existing ParallelFetcher users to use PdmApplication
  tests: add helpers for building API-handler-level integration tests
  tests: add example tests for SDN API routes
  api-cache: add wrapper type
  context: provide api-cache on the app object
  api: subscriptions: use PdmApplication instead of globals
  pdm-config: subscriptions: drop unused accessor functions
  tests: add example tests for remote subscription management

 cli/admin/src/main.rs                         |  10 +-
 lib/pdm-config/src/remotes.rs                 |  26 +-
 lib/pdm-config/src/subscriptions.rs           |  59 +--
 server/src/api/nodes/subscription.rs          |  23 +-
 server/src/api/nodes/tasks.rs                 |   2 +-
 server/src/api/pbs/mod.rs                     |  10 +-
 server/src/api/pve/firewall.rs                |  53 +--
 server/src/api/pve/mod.rs                     |   3 +-
 server/src/api/remotes/mod.rs                 |   5 +-
 server/src/api/remotes/updates.rs             |   3 +-
 server/src/api/resources.rs                   |  39 +-
 server/src/api/sdn/controllers.rs             |  17 +-
 server/src/api/sdn/vnets.rs                   |  20 +-
 server/src/api/sdn/zones.rs                   |  17 +-
 server/src/api/subscriptions/mod.rs           | 223 +++++++-----
 server/src/api_cache.rs                       | 117 ++++--
 server/src/bin/proxmox-datacenter-api/main.rs |  17 +-
 ...proxmox-datacenter-manager-daily-update.rs |  12 +-
 .../bin/proxmox-datacenter-privileged-api.rs  |  22 +-
 server/src/connection.rs                      |  54 ++-
 server/src/context.rs                         |  49 ---
 server/src/context/default.rs                 |   5 +
 server/src/context/faked_remotes.rs           |  42 +++
 server/src/context/mod.rs                     | 165 +++++++++
 server/src/metric_collection/mod.rs           |   6 +-
 .../remote_collection_task.rs                 |  47 +--
 server/src/parallel_fetcher.rs                | 109 +++++-
 server/src/remote_tasks/refresh_task.rs       |  27 +-
 server/src/remote_updates.rs                  |  17 +-
 server/src/test_support/fake_remote.rs        |   8 +-
 .../pve/remote-a/list_vnets.json              |   8 +
 .../pve/remote-a/list_zones.json              |   8 +
 .../pve/remote-b/list_vnets.json              |   8 +
 .../pve/remote-b/list_zones.json              |   8 +
 server/tests/common/environment.rs            |  28 ++
 server/tests/common/mod.rs                    | 111 ++++++
 server/tests/common/test_application.rs       | 337 ++++++++++++++++++
 server/tests/test_sdn.rs                      |  86 +++++
 server/tests/test_subscriptions.rs            | 122 +++++++
 39 files changed, 1509 insertions(+), 414 deletions(-)
 delete mode 100644 server/src/context.rs
 create mode 100644 server/src/context/default.rs
 create mode 100644 server/src/context/faked_remotes.rs
 create mode 100644 server/src/context/mod.rs
 create mode 100644 server/tests/api_responses/pve/remote-a/list_vnets.json
 create mode 100644 server/tests/api_responses/pve/remote-a/list_zones.json
 create mode 100644 server/tests/api_responses/pve/remote-b/list_vnets.json
 create mode 100644 server/tests/api_responses/pve/remote-b/list_zones.json
 create mode 100644 server/tests/common/environment.rs
 create mode 100644 server/tests/common/mod.rs
 create mode 100644 server/tests/common/test_application.rs
 create mode 100644 server/tests/test_sdn.rs
 create mode 100644 server/tests/test_subscriptions.rs


Summary over all repositories:
  51 files changed, 2321 insertions(+), 427 deletions(-)

-- 
Generated by murpp 0.12.1




             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 Lukas Wagner [this message]
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 ` [PATCH datacenter-manager v3 14/21] server: migrate existing ParallelFetcher users to use PdmApplication Lukas Wagner
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-1-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