lmrc_kubernetes/
error.rs

1//! Error types for the Kubernetes manager library.
2//!
3//! This module defines all error types that can occur during Kubernetes operations,
4//! providing detailed context for debugging and error handling.
5
6use thiserror::Error;
7
8/// Result type alias for Kubernetes operations.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Main error type for all Kubernetes operations.
12#[derive(Error, Debug)]
13pub enum Error {
14    /// Error connecting to the Kubernetes cluster.
15    #[error("Failed to connect to Kubernetes cluster: {0}")]
16    ConnectionError(String),
17
18    /// Error with Kubernetes configuration (kubeconfig, credentials, etc.).
19    #[error("Configuration error: {0}")]
20    ConfigurationError(String),
21
22    /// Error during deployment operations.
23    #[error("Deployment failed: {0}")]
24    DeploymentError(String),
25
26    /// Error during rollback operations.
27    #[error("Rollback failed: {0}")]
28    RollbackError(String),
29
30    /// Error during scaling operations.
31    #[error("Scaling failed: {0}")]
32    ScalingError(String),
33
34    /// Error during service operations.
35    #[error("Service operation failed: {0}")]
36    ServiceError(String),
37
38    /// Error during secret operations.
39    #[error("Secret operation failed: {0}")]
40    SecretError(String),
41
42    /// Error during namespace operations.
43    #[error("Namespace operation failed: {0}")]
44    NamespaceError(String),
45
46    /// Error during ConfigMap operations.
47    #[error("ConfigMap operation failed: {0}")]
48    ConfigMapError(String),
49
50    /// Error during Ingress operations.
51    #[error("Ingress operation failed: {0}")]
52    IngressError(String),
53
54    /// Error during Gateway operations.
55    #[error("Gateway operation failed: {0}")]
56    GatewayError(String),
57
58    /// Validation error for resource specs.
59    #[error("Validation error: {0}")]
60    ValidationError(String),
61
62    /// Error during NetworkPolicy operations.
63    #[error("NetworkPolicy operation failed: {0}")]
64    NetworkPolicyError(String),
65
66    /// Error during Job operations.
67    #[error("Job operation failed: {0}")]
68    JobError(String),
69
70    /// Error during CronJob operations.
71    #[error("CronJob operation failed: {0}")]
72    CronJobError(String),
73
74    /// Error during PersistentVolumeClaim operations.
75    #[error("PersistentVolumeClaim operation failed: {0}")]
76    PvcError(String),
77
78    /// Error during HorizontalPodAutoscaler operations.
79    #[error("HorizontalPodAutoscaler operation failed: {0}")]
80    HpaError(String),
81
82    /// Resource not found in the cluster.
83    #[error("Resource not found: {kind} '{name}' in namespace '{namespace}'")]
84    ResourceNotFound {
85        /// Kind of resource.
86        kind: String,
87        /// Name of resource.
88        name: String,
89        /// Namespace where resource was searched.
90        namespace: String,
91    },
92
93    /// Resource already exists.
94    #[error("Resource already exists: {kind} '{name}' in namespace '{namespace}'")]
95    ResourceAlreadyExists {
96        /// Kind of resource.
97        kind: String,
98        /// Name of resource.
99        name: String,
100        /// Namespace where resource exists.
101        namespace: String,
102    },
103
104    /// Timeout waiting for a resource to be ready.
105    #[error("Timeout waiting for {resource} to be ready after {timeout_secs}s: {details}")]
106    Timeout {
107        /// Resource name.
108        resource: String,
109        /// Timeout duration in seconds.
110        timeout_secs: u64,
111        /// Additional details.
112        details: String,
113    },
114
115    /// Pod failed with an error.
116    #[error("Pod '{pod_name}' failed: {reason}")]
117    PodFailed {
118        /// Pod name.
119        pod_name: String,
120        /// Failure reason.
121        reason: String,
122    },
123
124    /// Container crashed.
125    #[error(
126        "Container '{container}' in pod '{pod}' crashed: exit code {exit_code}, restarts: {restarts}"
127    )]
128    ContainerCrashed {
129        /// Pod name.
130        pod: String,
131        /// Container name.
132        container: String,
133        /// Exit code.
134        exit_code: i32,
135        /// Restart count.
136        restarts: i32,
137    },
138
139    /// Image pull error.
140    #[error("Failed to pull image '{image}': {reason}")]
141    ImagePullError {
142        /// Image name.
143        image: String,
144        /// Error reason.
145        reason: String,
146    },
147
148    /// Invalid resource specification.
149    #[error("Invalid resource specification: {0}")]
150    InvalidSpec(String),
151
152    /// Serialization/deserialization error.
153    #[error("Serialization error: {0}")]
154    SerializationError(String),
155
156    /// Underlying Kubernetes API error.
157    #[error("Kubernetes API error: {0}")]
158    KubeError(#[from] kube::Error),
159
160    /// Generic internal error.
161    #[error("Internal error: {0}")]
162    Internal(String),
163}
164
165impl Error {
166    /// Create a connection error.
167    pub fn connection<S: Into<String>>(msg: S) -> Self {
168        Self::ConnectionError(msg.into())
169    }
170
171    /// Create a configuration error.
172    pub fn config<S: Into<String>>(msg: S) -> Self {
173        Self::ConfigurationError(msg.into())
174    }
175
176    /// Create a deployment error.
177    pub fn deployment<S: Into<String>>(msg: S) -> Self {
178        Self::DeploymentError(msg.into())
179    }
180
181    /// Create a rollback error.
182    pub fn rollback<S: Into<String>>(msg: S) -> Self {
183        Self::RollbackError(msg.into())
184    }
185
186    /// Create a scaling error.
187    pub fn scaling<S: Into<String>>(msg: S) -> Self {
188        Self::ScalingError(msg.into())
189    }
190
191    /// Create a service error.
192    pub fn service<S: Into<String>>(msg: S) -> Self {
193        Self::ServiceError(msg.into())
194    }
195
196    /// Create a secret error.
197    pub fn secret<S: Into<String>>(msg: S) -> Self {
198        Self::SecretError(msg.into())
199    }
200
201    /// Create a namespace error.
202    pub fn namespace<S: Into<String>>(msg: S) -> Self {
203        Self::NamespaceError(msg.into())
204    }
205
206    /// Create a ConfigMap error.
207    pub fn configmap<S: Into<String>>(msg: S) -> Self {
208        Self::ConfigMapError(msg.into())
209    }
210
211    /// Create an Ingress error.
212    pub fn ingress<S: Into<String>>(msg: S) -> Self {
213        Self::IngressError(msg.into())
214    }
215
216    /// Create a Gateway error.
217    pub fn gateway<S: Into<String>>(msg: S) -> Self {
218        Self::GatewayError(msg.into())
219    }
220
221    /// Create a validation error.
222    pub fn validation<S: Into<String>>(msg: S) -> Self {
223        Self::ValidationError(msg.into())
224    }
225
226    /// Create a NetworkPolicy error.
227    pub fn network_policy<S: Into<String>>(msg: S) -> Self {
228        Self::NetworkPolicyError(msg.into())
229    }
230
231    /// Create a Job error.
232    pub fn job<S: Into<String>>(msg: S) -> Self {
233        Self::JobError(msg.into())
234    }
235
236    /// Create a CronJob error.
237    pub fn cronjob<S: Into<String>>(msg: S) -> Self {
238        Self::CronJobError(msg.into())
239    }
240
241    /// Create a PVC error.
242    pub fn pvc<S: Into<String>>(msg: S) -> Self {
243        Self::PvcError(msg.into())
244    }
245
246    /// Create an HPA error.
247    pub fn hpa<S: Into<String>>(msg: S) -> Self {
248        Self::HpaError(msg.into())
249    }
250
251    /// Create a resource not found error.
252    pub fn not_found<S: Into<String>>(kind: S, name: S, namespace: S) -> Self {
253        Self::ResourceNotFound {
254            kind: kind.into(),
255            name: name.into(),
256            namespace: namespace.into(),
257        }
258    }
259
260    /// Create a resource already exists error.
261    pub fn already_exists<S: Into<String>>(kind: S, name: S, namespace: S) -> Self {
262        Self::ResourceAlreadyExists {
263            kind: kind.into(),
264            name: name.into(),
265            namespace: namespace.into(),
266        }
267    }
268
269    /// Create a timeout error.
270    pub fn timeout<S: Into<String>>(resource: S, timeout_secs: u64, details: S) -> Self {
271        Self::Timeout {
272            resource: resource.into(),
273            timeout_secs,
274            details: details.into(),
275        }
276    }
277
278    /// Create a pod failed error.
279    pub fn pod_failed<S: Into<String>>(pod_name: S, reason: S) -> Self {
280        Self::PodFailed {
281            pod_name: pod_name.into(),
282            reason: reason.into(),
283        }
284    }
285
286    /// Create a container crashed error.
287    pub fn container_crashed<S: Into<String>>(
288        pod: S,
289        container: S,
290        exit_code: i32,
291        restarts: i32,
292    ) -> Self {
293        Self::ContainerCrashed {
294            pod: pod.into(),
295            container: container.into(),
296            exit_code,
297            restarts,
298        }
299    }
300
301    /// Create an image pull error.
302    pub fn image_pull<S1: Into<String>, S2: Into<String>>(image: S1, reason: S2) -> Self {
303        Self::ImagePullError {
304            image: image.into(),
305            reason: reason.into(),
306        }
307    }
308
309    /// Create an invalid spec error.
310    pub fn invalid_spec<S: Into<String>>(msg: S) -> Self {
311        Self::InvalidSpec(msg.into())
312    }
313
314    /// Create an internal error.
315    pub fn internal<S: Into<String>>(msg: S) -> Self {
316        Self::Internal(msg.into())
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323
324    #[test]
325    fn test_error_construction() {
326        let err = Error::connection("test connection failed");
327        assert!(err.to_string().contains("test connection failed"));
328
329        let err = Error::not_found("Deployment", "my-app", "default");
330        assert!(err.to_string().contains("my-app"));
331        assert!(err.to_string().contains("default"));
332
333        let err = Error::timeout("deployment/my-app", 120, "pods not ready");
334        assert!(err.to_string().contains("120s"));
335    }
336
337    #[test]
338    fn test_container_crashed_error() {
339        let err = Error::container_crashed("my-pod", "app", 137, 3);
340        let msg = err.to_string();
341        assert!(msg.contains("my-pod"));
342        assert!(msg.contains("app"));
343        assert!(msg.contains("137"));
344        assert!(msg.contains("3"));
345    }
346
347    #[test]
348    fn test_image_pull_error() {
349        let err = Error::image_pull("registry.io/myapp:v1", "ImagePullBackOff");
350        let msg = err.to_string();
351        assert!(msg.contains("registry.io/myapp:v1"));
352        assert!(msg.contains("ImagePullBackOff"));
353    }
354}