manta_server/
dispatcher.rs

1//! Runtime backend selector — wraps either a CSM or an OpenCHAMI
2//! backend behind a single enum so the rest of the codebase is
3//! backend-agnostic.
4//!
5//! [`StaticBackendDispatcher`] is constructed once per configured site
6//! at startup (see [`crate::config::Site`]) and stored in the request
7//! `ServerState`. Each HTTP request resolves a borrowed `InfraContext`
8//! that holds a reference to the dispatcher for the requested site;
9//! service code calls trait methods on that reference, and the trait
10//! impls in [`crate::backend_dispatcher`] route the call to the active
11//! variant.
12
13use csm_rs::ShastaClient;
14use manta_backend_dispatcher::error::Error;
15use ochami_rs::backend_connector::Ochami;
16
17/// Routes API calls to either a CSM or OCHAMI backend.
18///
19/// Every backend trait (e.g. `CfsTrait`, `GroupTrait`,
20/// `BootParametersTrait`) is implemented for this enum under
21/// [`crate::backend_dispatcher`]. The impls use the `dispatch!` macro
22/// to forward the call to the wrapped client; both variants implement
23/// the same trait surface so service code never branches on backend
24/// kind.
25///
26/// Cloning is cheap — both inner clients are `Arc`-shaped internally.
27/// Service helpers that need to move the dispatcher into a `'static`
28/// spawned task call `InfraContext::backend_clone()` (see
29/// [`crate::service::infra_backend`]).
30#[derive(Clone, Debug)]
31#[allow(clippy::upper_case_acronyms)]
32pub enum StaticBackendDispatcher {
33  /// HPE Cray System Management (CSM) backend, used by Alps-style
34  /// deployments. Wraps a `csm-rs` HTTP client (`ShastaClient`).
35  CSM(ShastaClient),
36  /// OpenCHAMI backend, used by sites running the open-source CSM
37  /// alternative. Wraps an `ochami-rs` HTTP client.
38  OCHAMI(Ochami),
39}
40
41impl StaticBackendDispatcher {
42  /// Returns `"csm"` or `"ochami"` for the currently-selected variant.
43  /// Cheap, infallible — intended for use as a structured `tracing` field.
44  pub fn backend_kind(&self) -> &'static str {
45    match self {
46      Self::CSM(_) => "csm",
47      Self::OCHAMI(_) => "ochami",
48    }
49  }
50
51  /// Create a new dispatcher for the given backend type.
52  ///
53  /// `backend_type` must be `"csm"` or `"ochami"` (matching
54  /// [`crate::config::BackendTechnology::as_str`]); any other value
55  /// returns [`Error::UnsupportedBackend`]. `root_cert` is the PEM
56  /// bytes of the backend's root CA — used to verify TLS to
57  /// `base_url`. `socks5_proxy` is an optional SOCKS5 URL applied to
58  /// every outbound request.
59  ///
60  /// Called once per configured site during server startup.
61  ///
62  /// # Errors
63  ///
64  /// - [`Error::UnsupportedBackend`] when `backend_type` is not
65  ///   `"csm"` or `"ochami"`.
66  /// - Any error surfaced by `ShastaClient::new` (CSM variant) when
67  ///   the supplied cert or proxy URL is unusable.
68  pub fn new(
69    backend_type: &str,
70    base_url: &str,
71    root_cert: &[u8],
72    socks5_proxy: Option<&str>,
73  ) -> Result<Self, Error> {
74    match backend_type {
75      "csm" => Ok(Self::CSM(ShastaClient::new(
76        base_url,
77        root_cert,
78        socks5_proxy.map(str::to_string),
79      )?)),
80      "ochami" => {
81        Ok(Self::OCHAMI(Ochami::new(base_url, root_cert, socks5_proxy)))
82      }
83      _ => Err(Error::UnsupportedBackend(format!(
84        "Backend '{backend_type}' not supported"
85      ))),
86    }
87  }
88}