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    /// Connection to cluster failed
57    #[error("Connection to cluster failed: {0}")]
58    ConnectionFailed(String),
59
60    /// Cluster operation error
61    #[error("Cluster operation error: {0}")]
62    ClusterError(String),
63
64    /// Topic operation failed
65    #[error("Topic operation failed: {0}")]
66    TopicError(String),
67
68    /// Connect operation failed
69    #[error("Connect operation failed: {0}")]
70    ConnectError(String),
71}
72
73/// Result type for operator operations
74pub type Result<T> = std::result::Result<T, OperatorError>;
75
76impl OperatorError {
77    /// Check if this error is retryable
78    pub fn is_retryable(&self) -> bool {
79        matches!(
80            self,
81            OperatorError::KubeError(_)
82                | OperatorError::Timeout(_)
83                | OperatorError::ReconcileFailed(_)
84        )
85    }
86
87    /// Get a suggested requeue delay for retryable errors
88    pub fn requeue_delay(&self) -> Option<std::time::Duration> {
89        if self.is_retryable() {
90            Some(std::time::Duration::from_secs(30))
91        } else {
92            None
93        }
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_error_display() {
103        let err = OperatorError::NotFound {
104            kind: "StatefulSet".to_string(),
105            name: "rivven-cluster".to_string(),
106            namespace: "default".to_string(),
107        };
108        assert!(err.to_string().contains("StatefulSet"));
109        assert!(err.to_string().contains("rivven-cluster"));
110    }
111
112    #[test]
113    fn test_retryable_errors() {
114        let timeout_err = OperatorError::Timeout("test".to_string());
115        assert!(timeout_err.is_retryable());
116
117        let validation_err = OperatorError::ValidationError("test".to_string());
118        assert!(!validation_err.is_retryable());
119    }
120
121    #[test]
122    fn test_requeue_delay() {
123        let retryable = OperatorError::Timeout("test".to_string());
124        assert!(retryable.requeue_delay().is_some());
125
126        let not_retryable = OperatorError::InvalidConfig("test".to_string());
127        assert!(not_retryable.requeue_delay().is_none());
128    }
129}