Skip to main content

xianyun_cli/
drivers.rs

1//! Cluster driver and deployer registration.
2//!
3//! Port of the side effect Go gets for free from each driver and deployer
4//! package's `func init()` (see `internal/cluster/{colima,k3d,vcluster,
5//! external,auto}/driver.go` and `internal/deployer/{helm,kustomize,
6//! namespace}/manager.go`): registering itself with the matching registry
7//! just by being imported. Rust has no import-time side effects, so
8//! `xianyun-cli` — the one binary that actually needs everything wired up —
9//! does the registration explicitly, once per process.
10//!
11//! Without the deployer half, `deployer::registry::new_manager` fails with
12//! `UnknownType` for every configured deployer, so `up` and `deploy` can
13//! install nothing (GAP-001 in the specification).
14//!
15//! Test binaries need this too (a `caproni.yaml` with `cluster.driver:
16//! mock` has to resolve to something), so [`ensure_registered`] is public
17//! and idempotent rather than being private, main-only setup.
18
19use std::sync::{Arc, Once};
20
21use xianyun_backends::helm::Client as HelmClient;
22use xianyun_backends::kubectl::Client as KubectlClient;
23use xianyun_backends::kustomize::{Client as KustomizeClient, DiskFileSystem};
24use xianyun_cluster::driver::{Driver, auto, colima, external, k3d, mock, vcluster};
25use xianyun_cluster::registry::register_driver;
26use xianyun_deploy::deployer::{self, Manager as DeployerManager};
27use xianyun_executor::exec::{Client, CommandRunner};
28
29static INIT: Once = Once::new();
30
31/// Registers every built-in cluster driver with `xianyun_cluster`'s
32/// registry, and every built-in deployer (`helm`, `kustomize`, `namespace`)
33/// with `xianyun_deploy`'s. Safe to call from multiple test binaries and multiple times
34/// within the same process; only the first call does anything.
35pub fn ensure_registered() {
36    INIT.call_once(|| {
37        register_driver("mock", |cfg, profile_name| {
38            Box::new(mock::Driver::new(cfg, profile_name)) as Box<dyn Driver>
39        });
40        register_driver("colima", |cfg, profile_name| {
41            Box::new(colima::Driver::new(cfg, profile_name)) as Box<dyn Driver>
42        });
43        register_driver("k3d", |cfg, profile_name| {
44            Box::new(k3d::Driver::new(cfg, profile_name)) as Box<dyn Driver>
45        });
46        register_driver("vcluster", |cfg, profile_name| {
47            let runner: Arc<dyn CommandRunner + Send + Sync> = Arc::new(Client);
48            Box::new(vcluster::Driver::new(cfg, profile_name, runner)) as Box<dyn Driver>
49        });
50        register_driver("external", |cfg, _profile_name| {
51            let runner: Arc<dyn CommandRunner + Send + Sync> = Arc::new(Client);
52            Box::new(external::Driver::new(cfg, runner)) as Box<dyn Driver>
53        });
54        register_driver("auto", |cfg, profile_name| {
55            auto::new_auto_manager(std::env::consts::OS, cfg, profile_name)
56        });
57
58        register_deployers();
59    });
60}
61
62/// Each factory receives the cluster's kubeconfig (its YAML content, as
63/// `Driver::get_kubeconfig` returns it) and builds a manager whose backends
64/// run every tool through the real command runner.
65fn register_deployers() {
66    deployer::registry::register(
67        "helm",
68        Box::new(|kubeconfig| {
69            let runner: Arc<dyn CommandRunner + Send + Sync> = Arc::new(Client);
70            Box::new(deployer::helm::manager::Manager::new(HelmClient::new(
71                kubeconfig, runner,
72            ))) as Box<dyn DeployerManager + Send + Sync>
73        }),
74    );
75
76    deployer::registry::register(
77        "kustomize",
78        Box::new(|kubeconfig| {
79            let runner: Arc<dyn CommandRunner + Send + Sync> = Arc::new(Client);
80            let kustomize = KustomizeClient::new(Arc::new(DiskFileSystem), runner.clone());
81            let kubectl = KubectlClient::with_config(runner, kubeconfig);
82            Box::new(deployer::kustomize::manager::Manager::new(
83                Box::new(kustomize),
84                Box::new(kubectl),
85            )) as Box<dyn DeployerManager + Send + Sync>
86        }),
87    );
88
89    deployer::registry::register(
90        "namespace",
91        Box::new(|kubeconfig| {
92            let runner: Arc<dyn CommandRunner + Send + Sync> = Arc::new(Client);
93            let kubectl = KubectlClient::with_config(runner, kubeconfig);
94            Box::new(deployer::namespace::manager::Manager::new(Box::new(
95                kubectl,
96            ))) as Box<dyn DeployerManager + Send + Sync>
97        }),
98    );
99}