docker_image_pusher/error/
handlers.rs1use crate::error::{RegistryError, Result};
4use reqwest::StatusCode;
5use std::time::Duration;
6
7pub struct HttpErrorHandler;
9
10impl HttpErrorHandler {
11 pub fn handle_upload_error(
13 status: StatusCode,
14 error_text: &str,
15 context: &str,
16 ) -> RegistryError {
17 let error_msg = match status.as_u16() {
18 400 => {
19 if error_text.contains("exist blob require digest") {
20 format!(
21 "Digest validation failed - Registry reports blob exists but digest mismatch: {}",
22 error_text
23 )
24 } else if error_text.contains("BAD_REQUEST") {
25 format!(
26 "Bad request - Check digest format and data integrity: {}",
27 error_text
28 )
29 } else {
30 format!("Bad request during {}: {}", context, error_text)
31 }
32 }
33 401 => format!("Authentication failed during {}: {}", context, error_text),
34 403 => format!("Permission denied for {}: {}", context, error_text),
35 404 => format!(
36 "Repository not found or {} session expired: {}",
37 context, error_text
38 ),
39 409 => format!(
40 "Conflict - Blob already exists with different digest: {}",
41 error_text
42 ),
43 413 => format!("File too large for {}: {}", context, error_text),
44 422 => format!("Invalid digest or data for {}: {}", context, error_text),
45 500 => format!("Registry server error during {}: {}", context, error_text),
46 502 | 503 => format!("Registry unavailable during {}: {}", context, error_text),
47 507 => format!("Registry out of storage during {}: {}", context, error_text),
48 508 => format!("Streaming {} timeout: {}", context, error_text),
49 _ => format!("{} failed (status {}): {}", context, status, error_text),
50 };
51
52 RegistryError::Upload(error_msg)
53 }
54
55 pub fn handle_auth_error(status: StatusCode, error_text: &str) -> RegistryError {
57 let error_msg = match status.as_u16() {
58 400 => "Invalid token request parameters".to_string(),
59 401 => "Invalid credentials provided".to_string(),
60 403 => "Access denied - insufficient permissions".to_string(),
61 404 => "Authentication endpoint not found".to_string(),
62 _ => format!("Authentication failed (status {}): {}", status, error_text),
63 };
64
65 RegistryError::Auth(error_msg)
66 }
67
68 pub fn handle_registry_error(
70 status: StatusCode,
71 error_text: &str,
72 operation: &str,
73 ) -> RegistryError {
74 let error_msg = match status.as_u16() {
75 401 => format!(
76 "Unauthorized to perform {} operation: {}",
77 operation, error_text
78 ),
79 403 => format!(
80 "Forbidden: insufficient permissions for {}: {}",
81 operation, error_text
82 ),
83 404 => format!("Resource not found for {}: {}", operation, error_text),
84 429 => format!("Rate limited during {}: {}", operation, error_text),
85 500 => format!("Registry server error during {}: {}", operation, error_text),
86 502 | 503 => format!("Registry unavailable for {}: {}", operation, error_text),
87 _ => format!("{} failed (status {}): {}", operation, status, error_text),
88 };
89
90 RegistryError::Registry(error_msg)
91 }
92
93 pub fn handle_streaming_error(status: StatusCode, error_text: &str) -> RegistryError {
95 let error_msg = match status.as_u16() {
96 400 => {
97 if error_text.contains("DIGEST_INVALID") {
98 "Digest validation failed - Registry reports uploaded content doesn't match expected digest".to_string()
99 } else {
100 format!("Bad request during streaming upload: {}", error_text)
101 }
102 }
103 413 => "File too large for registry".to_string(),
104 507 => "Insufficient storage space on registry".to_string(),
105 401 => "Authentication failed during upload".to_string(),
106 403 => "Permission denied for upload".to_string(),
107 408 | 504 => "Streaming upload timeout".to_string(),
108 500 => {
109 if error_text.contains("s3aws") || error_text.contains("DriverName") {
110 format!(
111 "Registry storage backend error (S3): {}. This is typically a temporary issue with the registry's storage system",
112 error_text
113 )
114 } else {
115 format!("Registry internal server error: {}", error_text)
116 }
117 }
118 502 | 503 => format!("Registry temporarily unavailable: {}", error_text),
119 _ => format!(
120 "Streaming upload failed (status {}): {}",
121 status, error_text
122 ),
123 };
124
125 RegistryError::Upload(error_msg)
126 }
127
128 pub fn is_storage_backend_error(error_text: &str) -> bool {
130 error_text.contains("s3aws")
131 || error_text.contains("DriverName")
132 || error_text.contains("storage backend")
133 || error_text.contains("500 Internal Server Error")
134 }
135
136 pub fn get_storage_error_retry_delay(attempt: u32, base_delay_secs: u64) -> Duration {
138 let backoff_multiplier = 2_u64.pow(attempt.min(4));
139 Duration::from_secs(base_delay_secs * backoff_multiplier)
140 }
141}
142
143pub struct NetworkErrorHandler;
145
146impl NetworkErrorHandler {
147 pub fn handle_network_error(error: &reqwest::Error, context: &str) -> RegistryError {
149 if error.is_timeout() {
150 RegistryError::Network(format!("Timeout during {}", context))
151 } else if error.is_connect() {
152 RegistryError::Network(format!("Connection failed during {}", context))
153 } else {
154 RegistryError::Network(format!("Network error during {}: {}", context, error))
155 }
156 }
157}
158
159pub struct ValidationErrorHandler;
161
162impl ValidationErrorHandler {
163 pub fn validate_required_field(field_name: &str, value: &Option<String>) -> Result<()> {
165 if value.is_none() || value.as_ref().unwrap().is_empty() {
166 Err(RegistryError::Validation(format!(
167 "{} is required",
168 field_name
169 )))
170 } else {
171 Ok(())
172 }
173 }
174}
175
176#[macro_export]
178macro_rules! with_context {
179 ($result:expr, $context:expr) => {
180 $result.map_err(|e| match e {
181 RegistryError::Auth(msg) => RegistryError::Auth(format!("{}: {}", $context, msg)),
182 RegistryError::Network(msg) => RegistryError::Network(format!("{}: {}", $context, msg)),
183 RegistryError::Upload(msg) => RegistryError::Upload(format!("{}: {}", $context, msg)),
184 RegistryError::Io(msg) => RegistryError::Io(format!("{}: {}", $context, msg)),
185 RegistryError::Parse(msg) => RegistryError::Parse(format!("{}: {}", $context, msg)),
186 RegistryError::Registry(msg) => {
187 RegistryError::Registry(format!("{}: {}", $context, msg))
188 }
189 RegistryError::ImageParsing(msg) => {
190 RegistryError::ImageParsing(format!("{}: {}", $context, msg))
191 }
192 RegistryError::Validation(msg) => {
193 RegistryError::Validation(format!("{}: {}", $context, msg))
194 }
195 RegistryError::Cache { message, path } => RegistryError::Cache {
196 message: format!("{}: {}", $context, message),
197 path,
198 },
199 RegistryError::NotFound(msg) => {
200 RegistryError::NotFound(format!("{}: {}", $context, msg))
201 }
202 })
203 };
204}