Skip to main content

rivven_operator/
error.rs

1//! Error types for the Rivven Kubernetes Operator
2
3use thiserror::Error;
4
5/// Errors that can occur during operator operations
6#[derive(Error, Debug)]
7pub enum OperatorError {
8    /// Kubernetes API error
9    #[error("Kubernetes API error: {0}")]
10    KubeError(#[from] kube::Error),
11
12    /// Resource not found
13    #[error("Resource not found: {kind}/{name} in namespace {namespace}")]
14    NotFound {
15        kind: String,
16        name: String,
17        namespace: String,
18    },
19
20    /// Invalid configuration
21    #[error("Invalid configuration: {0}")]
22    InvalidConfig(String),
23
24    /// Reconciliation failed
25    #[error("Reconciliation failed: {0}")]
26    ReconcileFailed(String),
27
28    /// Serialization error
29    #[error("Serialization error: {0}")]
30    SerializationError(#[from] serde_json::Error),
31
32    /// YAML serialization error
33    #[error("YAML serialization error: {0}")]
34    YamlError(#[from] serde_yaml::Error),
35
36    /// Validation error
37    #[error("Validation error: {0}")]
38    ValidationError(String),
39
40    /// Finalizer error
41    #[error("Finalizer error: {0}")]
42    FinalizerError(String),
43
44    /// Timeout error
45    #[error("Operation timed out: {0}")]
46    Timeout(String),
47
48    /// Internal error
49    #[error("Internal error: {0}")]
50    Internal(String),
51
52    /// Cluster not found
53    #[error("RivvenCluster not found: {0}")]
54    ClusterNotFound(String),
55
56    /// Topic operation failed
57    #[error("Topic operation failed: {0}")]
58    TopicError(String),
59
60    /// Connect operation failed
61    #[error("Connect operation failed: {0}")]
62    ConnectError(String),
63}
64
65/// Result type for operator operations
66pub type Result<T> = std::result::Result<T, OperatorError>;
67
68impl OperatorError {
69    /// Check if this error is retryable
70    pub fn is_retryable(&self) -> bool {
71        matches!(
72            self,
73            OperatorError::KubeError(_)
74                | OperatorError::Timeout(_)
75                | OperatorError::ReconcileFailed(_)
76        )
77    }
78
79    /// Get a suggested requeue delay for retryable errors
80    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}