1use thiserror::Error;
4
5#[derive(Error, Debug)]
7pub enum OperatorError {
8 #[error("Kubernetes API error: {0}")]
10 KubeError(#[from] kube::Error),
11
12 #[error("Resource not found: {kind}/{name} in namespace {namespace}")]
14 NotFound {
15 kind: String,
16 name: String,
17 namespace: String,
18 },
19
20 #[error("Invalid configuration: {0}")]
22 InvalidConfig(String),
23
24 #[error("Reconciliation failed: {0}")]
26 ReconcileFailed(String),
27
28 #[error("Serialization error: {0}")]
30 SerializationError(#[from] serde_json::Error),
31
32 #[error("YAML serialization error: {0}")]
34 YamlError(#[from] serde_yaml::Error),
35
36 #[error("Validation error: {0}")]
38 ValidationError(String),
39
40 #[error("Finalizer error: {0}")]
42 FinalizerError(String),
43
44 #[error("Operation timed out: {0}")]
46 Timeout(String),
47
48 #[error("Internal error: {0}")]
50 Internal(String),
51
52 #[error("RivvenCluster not found: {0}")]
54 ClusterNotFound(String),
55
56 #[error("Topic operation failed: {0}")]
58 TopicError(String),
59
60 #[error("Connect operation failed: {0}")]
62 ConnectError(String),
63}
64
65pub type Result<T> = std::result::Result<T, OperatorError>;
67
68impl OperatorError {
69 pub fn is_retryable(&self) -> bool {
71 matches!(
72 self,
73 OperatorError::KubeError(_)
74 | OperatorError::Timeout(_)
75 | OperatorError::ReconcileFailed(_)
76 )
77 }
78
79 pub fn requeue_delay(&self) -> Option<std::time::Duration> {
81 if self.is_retryable() {
82 Some(std::time::Duration::from_secs(30))
83 } else {
84 None
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_error_display() {
95 let err = OperatorError::NotFound {
96 kind: "StatefulSet".to_string(),
97 name: "rivven-cluster".to_string(),
98 namespace: "default".to_string(),
99 };
100 assert!(err.to_string().contains("StatefulSet"));
101 assert!(err.to_string().contains("rivven-cluster"));
102 }
103
104 #[test]
105 fn test_retryable_errors() {
106 let timeout_err = OperatorError::Timeout("test".to_string());
107 assert!(timeout_err.is_retryable());
108
109 let validation_err = OperatorError::ValidationError("test".to_string());
110 assert!(!validation_err.is_retryable());
111 }
112
113 #[test]
114 fn test_requeue_delay() {
115 let retryable = OperatorError::Timeout("test".to_string());
116 assert!(retryable.requeue_delay().is_some());
117
118 let not_retryable = OperatorError::InvalidConfig("test".to_string());
119 assert!(not_retryable.requeue_delay().is_none());
120 }
121}