Skip to main content

xianyun_cluster/driver/
mod.rs

1//! Port of `internal/cluster/interface.go`, `internal/cluster/domain.go`, and
2//! `internal/cluster/errors.go`: the `Driver` trait every cluster backend
3//! implements, plus the shared status/error types.
4
5pub mod auto;
6pub mod colima;
7pub mod external;
8pub mod k3d;
9pub mod mock;
10pub mod vcluster;
11
12use std::time::Duration;
13use thiserror::Error;
14
15/// Port of `cluster.State`.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum State {
18    /// The cluster is not running.
19    Stopped,
20    /// The cluster is starting but not yet ready.
21    Starting,
22    /// The cluster is running but not ready to accept workloads.
23    NotReady,
24    /// The cluster is running and ready to accept workloads.
25    Ready,
26}
27
28impl State {
29    /// Returns the wire form used in status output, matching the Go string constants.
30    pub fn as_str(&self) -> &'static str {
31        match self {
32            State::Stopped => "stopped",
33            State::Starting => "starting",
34            State::NotReady => "not-ready",
35            State::Ready => "ready",
36        }
37    }
38}
39
40/// Port of `cluster.Status`.
41#[derive(Debug, Clone, Default)]
42pub struct Status {
43    pub state: Option<State>,
44    pub exists: bool,
45    pub driver: String,
46    pub kubernetes_version: String,
47    pub ip_address: String,
48    pub host_ip: String,
49    pub kube_context: String,
50}
51
52/// Port of `cluster.DNSReader`: the slice of a kubectl backend that DNS-zone
53/// sources read through.
54pub trait DnsReader {
55    fn get(&self, resource_type: &str, name: &str, namespace: &str) -> Result<String, DriverError>;
56    fn get_raw(&self, path: &str) -> Result<String, DriverError>;
57    fn run_job(
58        &self,
59        image: &str,
60        namespace: &str,
61        timeout: Duration,
62        command: &[&str],
63    ) -> Result<String, DriverError>;
64}
65
66/// Port of `cluster.DomainSource`: one place a cluster can be asked for the
67/// DNS zone it serves.
68#[derive(Clone, Copy)]
69pub struct DomainSource {
70    pub name: &'static str,
71    pub read: fn(&dyn DnsReader) -> Result<String, DriverError>,
72}
73
74/// Port of `cluster.Manager`: the interface every cluster driver implements.
75pub trait Driver {
76    /// Name returns the driver's registered name. Wrapping drivers such as
77    /// auto return the name of the driver they resolved to.
78    fn name(&self) -> &str;
79
80    /// Starts the cluster with its configured settings.
81    fn start(&self) -> Result<(), DriverError>;
82
83    /// Stops the running cluster gracefully.
84    fn stop(&self) -> Result<(), DriverError>;
85
86    /// Completely removes the cluster and all its data.
87    fn destroy(&self) -> Result<(), DriverError>;
88
89    /// Returns the current status of the cluster.
90    fn status(&self) -> Result<Status, DriverError>;
91
92    /// Returns the kubeconfig content for the cluster as a string.
93    fn get_kubeconfig(&self) -> Result<String, DriverError>;
94
95    /// Lists where to ask this driver's cluster for its DNS zone, in order;
96    /// `None` means the common chain.
97    fn domain_sources(&self) -> Option<Vec<DomainSource>> {
98        None
99    }
100}
101
102/// Port of `internal/cluster/errors.go`.
103#[derive(Debug, Clone, Error, PartialEq, Eq)]
104pub enum DriverError {
105    #[error("cluster is not running")]
106    ClusterNotRunning,
107    #[error("cluster is already running")]
108    ClusterAlreadyRunning,
109    #[error("cluster configuration mismatch")]
110    ConfigMismatch,
111    #[error("cluster driver not found: {0}")]
112    DriverNotFound(String),
113    #[error("driver configuration is missing")]
114    MissingDriverConfig,
115    #[error("host port is already in use: {0}")]
116    PortInUse(String),
117    #[error("{0}")]
118    Other(String),
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    /// `State` has no Go counterpart to port a test from: Go's `State` is a
126    /// typed `string` constant (`StateStopped State = "stopped"`), so
127    /// there's no conversion function to call. `as_str()` only exists
128    /// because Rust's `State` is a real enum; this is new coverage for it.
129    #[test]
130    fn state_as_str_matches_go_string_values() {
131        assert_eq!(State::Stopped.as_str(), "stopped");
132        assert_eq!(State::Starting.as_str(), "starting");
133        assert_eq!(State::NotReady.as_str(), "not-ready");
134        assert_eq!(State::Ready.as_str(), "ready");
135    }
136}