xianyun_deploy/deployer/mod.rs
1//! Interfaces for deploying applications to the caproni cluster.
2//!
3//! Ported from `internal/deployer/deployer.go` and `internal/deployer/errors.go`.
4
5use thiserror::Error;
6use xianyun_core::config::latest::{CaproniConfig, DeployerConfig};
7
8pub mod helm;
9pub mod kustomize;
10pub mod namespace;
11pub mod nix;
12pub mod registry;
13
14/// The current state of a deployment.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum State {
17 /// The deployer has not been deployed to the cluster.
18 NotDeployed,
19 /// The deployer is being deployed.
20 Deploying,
21 /// The deployer is deployed to the cluster.
22 Deployed,
23 /// The deployment failed.
24 Failed,
25}
26
27impl State {
28 /// The string form used in Go (`"not-deployed"`, etc.), for display
29 /// and any serialization boundary.
30 #[must_use]
31 pub fn as_str(&self) -> &'static str {
32 match self {
33 State::NotDeployed => "not-deployed",
34 State::Deploying => "deploying",
35 State::Deployed => "deployed",
36 State::Failed => "failed",
37 }
38 }
39}
40
41/// The current status of a deployment.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Status {
44 /// The current state of the deployment.
45 pub state: State,
46 /// The deployer type (e.g. `"helm"`).
47 pub deployer_type: String,
48}
49
50/// Errors common across deployer managers.
51#[derive(Debug, Error)]
52pub enum DeployerError {
53 /// Trying to create a deployer with an unknown type.
54 #[error("unknown deployer type")]
55 UnknownType,
56 /// A deployment operation failed.
57 #[error("deployment failed")]
58 DeployFailed,
59 /// An undeploy operation failed.
60 #[error("undeploy failed")]
61 UndeployFailed,
62 /// Unable to determine deployment status.
63 #[error("failed to get deployment status")]
64 StatusFailed,
65}
66
67/// The interface for managing deployment lifecycle. Implementations are
68/// responsible for deploying and managing applications using specific
69/// deployment strategies (e.g. Helm charts).
70pub trait Manager {
71 /// The deployer type that this manager handles (e.g. `"helm"`).
72 fn deployer_type(&self) -> String;
73
74 /// Deploys the application to the cluster. If the application is
75 /// already deployed, this updates it to match the current
76 /// configuration.
77 fn deploy(
78 &self,
79 deployer_config: &DeployerConfig,
80 cfg: Option<&xianyun_core::config::container::Container>,
81 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
82
83 /// Removes this deployment from the cluster.
84 fn undeploy(
85 &self,
86 deployer_config: &DeployerConfig,
87 caproni_config: &CaproniConfig,
88 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
89
90 /// Returns the current status of the deployment. Returns an error
91 /// if unable to determine deployment state.
92 fn status(
93 &self,
94 deployer_config: &DeployerConfig,
95 caproni_config: &CaproniConfig,
96 ) -> Result<Status, Box<dyn std::error::Error + Send + Sync>>;
97
98 /// Validates the deployer configuration and referenced files.
99 /// Returns `Ok(())` if valid, or an error describing validation
100 /// failures. Files in uncloned repository directories should not
101 /// cause validation to fail.
102 fn validate(
103 &self,
104 deployer_config: &DeployerConfig,
105 caproni_config: &CaproniConfig,
106 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 /// `State` has no Go counterpart to port a test from: Go's `State` is a
114 /// typed `string` constant (`StateNotDeployed State = "not-deployed"`),
115 /// so there's no conversion function to call. `as_str()` only exists
116 /// because Rust's `State` is a real enum; this is new coverage for it.
117 #[test]
118 fn state_as_str_matches_go_string_values() {
119 assert_eq!(State::NotDeployed.as_str(), "not-deployed");
120 assert_eq!(State::Deploying.as_str(), "deploying");
121 assert_eq!(State::Deployed.as_str(), "deployed");
122 assert_eq!(State::Failed.as_str(), "failed");
123 }
124}