-
Notifications
You must be signed in to change notification settings - Fork 150
feat: surface the swallowed errors across the fleet #3469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,12 +75,27 @@ impl Default for CarbideDhcpContext { | |
| Self { | ||
| api_endpoint: "https://[::1]:1079".to_string(), | ||
| nameservers: vec![Ipv4Addr::new(1, 1, 1, 1)], | ||
| forge_root_ca_path: std::env::var("FORGE_ROOT_CAFILE_PATH") | ||
| .unwrap_or_else(|_| tls_default::ROOT_CA.to_string()), | ||
| forge_client_cert_path: std::env::var("FORGE_CLIENT_CERT_PATH") | ||
| .unwrap_or_else(|_| tls_default::CLIENT_CERT.to_string()), | ||
| forge_client_key_path: std::env::var("FORGE_CLIENT_KEY_PATH") | ||
| .unwrap_or_else(|_| tls_default::CLIENT_KEY.to_string()), | ||
| forge_root_ca_path: std::env::var("FORGE_ROOT_CAFILE_PATH").unwrap_or_else(|e| { | ||
| log::warn!( | ||
| "FORGE_ROOT_CAFILE_PATH unset or unreadable ({e}); falling back to the built-in default root CA at {}", | ||
| tls_default::ROOT_CA | ||
| ); | ||
| tls_default::ROOT_CA.to_string() | ||
| }), | ||
| forge_client_cert_path: std::env::var("FORGE_CLIENT_CERT_PATH").unwrap_or_else(|e| { | ||
| log::warn!( | ||
| "FORGE_CLIENT_CERT_PATH unset or unreadable ({e}); falling back to the built-in default client certificate at {}", | ||
| tls_default::CLIENT_CERT | ||
| ); | ||
| tls_default::CLIENT_CERT.to_string() | ||
| }), | ||
| forge_client_key_path: std::env::var("FORGE_CLIENT_KEY_PATH").unwrap_or_else(|e| { | ||
| log::warn!( | ||
| "FORGE_CLIENT_KEY_PATH unset or unreadable ({e}); falling back to the built-in default client key at {}", | ||
| tls_default::CLIENT_KEY | ||
| ); | ||
| tls_default::CLIENT_KEY.to_string() | ||
| }), | ||
|
Comment on lines
+78
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Treat empty TLS-path environment variables as invalid.
Proposed approach- forge_root_ca_path: std::env::var("FORGE_ROOT_CAFILE_PATH").unwrap_or_else(|e| {
+ forge_root_ca_path: match std::env::var("FORGE_ROOT_CAFILE_PATH") {
+ Ok(path) if !path.is_empty() => path,
+ Ok(_) | Err(_) => {
+ // log the appropriate reason, then:
+ tls_default::ROOT_CA.to_string()
+ }
+ },Apply the same validation to the client certificate and key paths. As per path instructions, DHCP TLS-path fallbacks must be reviewed for configuration correctness and runtime behavior. 🤖 Prompt for AI AgentsSource: Path instructions |
||
| ntpservers: vec![ | ||
| Ipv4Addr::new(172, 20, 0, 24), | ||
| Ipv4Addr::new(172, 20, 0, 26), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -208,14 +208,18 @@ impl SyncLogFileWriter { | |
| tracing::info!(size = self.current_size, "rotating log file"); | ||
|
|
||
| // flush and drop the current file handle before renaming | ||
| if let Some(mut file) = self.current_file.take() { | ||
| let _ = file.flush(); | ||
| if let Some(mut file) = self.current_file.take() | ||
| && let Err(e) = file.flush() | ||
| { | ||
| tracing::warn!(error = %e, "failed to flush log file before rotation"); | ||
| } | ||
|
Comment on lines
+211
to
215
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: set -euo pipefail
# Map the file shape first, then inspect the relevant section.
ast-grep outline crates/health/src/sink/log_file.rs --view expanded || true
echo
wc -l crates/health/src/sink/log_file.rs
echo
sed -n '170,290p' crates/health/src/sink/log_file.rs | cat -nRepository: NVIDIA/infra-controller Length of output: 7202 🏁 Script executed: set -euo pipefail
# Inspect the call path and any rotation tests.
sed -n '1,170p' crates/health/src/sink/log_file.rs | cat -n
echo
sed -n '300,560p' crates/health/src/sink/log_file.rs | cat -n
echo
rg -n "rotate_if_needed|write_line\\(" crates/health/src/sink/log_file.rs crates/health/src -g '!target'Repository: NVIDIA/infra-controller Length of output: 18896 Do not continue rotation after a flush error. 🤖 Prompt for AI Agents |
||
|
|
||
| let current_path = self.log_path(); | ||
|
|
||
| if self.max_backups == 0 { | ||
| let _ = fs::remove_file(¤t_path); | ||
| if let Err(e) = fs::remove_file(¤t_path) { | ||
| tracing::warn!(error = %e, path = %current_path.display(), "failed to remove current log file during rotation"); | ||
| } | ||
| } else { | ||
| self.shift_backups(¤t_path); | ||
| } | ||
|
|
@@ -229,19 +233,25 @@ impl SyncLogFileWriter { | |
| for i in (1..self.max_backups).rev() { | ||
| let from = self.rotated_path(i); | ||
| let to = self.rotated_path(i + 1); | ||
| if from.exists() { | ||
| let _ = fs::rename(&from, &to); | ||
| if from.exists() | ||
| && let Err(e) = fs::rename(&from, &to) | ||
| { | ||
| tracing::warn!(error = %e, from = %from.display(), to = %to.display(), "failed to shift rotated log file"); | ||
| } | ||
| } | ||
|
|
||
| // current -> .1 | ||
| let backup = self.rotated_path(1); | ||
| let _ = fs::rename(current_path, &backup); | ||
| if let Err(e) = fs::rename(current_path, &backup) { | ||
| tracing::warn!(error = %e, from = %current_path.display(), to = %backup.display(), "failed to rotate current log file to backup"); | ||
| } | ||
|
|
||
| // prune the oldest backup beyond the limit | ||
| let oldest = self.rotated_path(self.max_backups + 1); | ||
| if oldest.exists() { | ||
| let _ = fs::remove_file(&oldest); | ||
| if oldest.exists() | ||
| && let Err(e) = fs::remove_file(&oldest) | ||
| { | ||
| tracing::warn!(error = %e, path = %oldest.display(), "failed to prune oldest rotated log file"); | ||
|
Comment on lines
+236
to
+254
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
file='crates/health/src/sink/log_file.rs'
echo "== file size =="
wc -l "$file"
echo "== relevant slice =="
sed -n '200,290p' "$file"
echo "== search exists/try_exists in file =="
rg -n 'try_exists|\.exists\(\)|rename\(|remove_file\(' "$file"Repository: NVIDIA/infra-controller Length of output: 4000 Avoid gating backup rotation on
🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 255
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 201
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 1878
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 9537
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 9308
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 4879
🏁 Script executed:
Repository: NVIDIA/infra-controller
Length of output: 161
🌐 Web query:
sqlx Transaction drop automatically rolls back rollback errors ignored documentation💡 Result:
In sqlx, when a Transaction object goes out of scope without being explicitly committed or rolled back, its Drop implementation triggers an automatic rollback [1][2]. Key points regarding this behavior include: 1. Automatic Rollback: If neither commit nor rollback is called before the transaction goes out of scope, the Transaction destructor (Drop) queues a rollback operation [1][2]. This is designed to ensure that pending transactions do not persist if an error occurs or the code path exits prematurely [1][2]. 2. Error Handling Limitations: Because the Drop trait in Rust is synchronous and cannot be async, the rollback operation triggered by dropping a transaction cannot be awaited [2][3]. Consequently, any errors that might occur during this automatic rollback cannot be captured or handled by your application code [3]. The rollback is effectively "fire-and-forget," and its success or failure remains opaque to the caller [2][3]. 3. Recommended Practice: Relying on the Drop implementation for rollback is considered a fallback mechanism rather than a best practice [3][4]. Since the automatic rollback relies on the underlying connection's state and future I/O, developers are encouraged to explicitly call.rollback.await or use helper functions like Connection::transaction (which handles commit/rollback automatically based on the result of the provided closure) to ensure predictable and reliable transaction lifecycle management [5][3]. 4. Performance Implications: Relying on Drop can lead to delayed rollbacks [3][4]. The rollback command is queued to be executed during the next asynchronous operation on that connection (or when the connection is returned to the pool and validated), which may result in database locks being held longer than necessary [3][4].
Citations:
🌐 Web query:
sqlx Transaction on Drop rollback silently ignore errors docs💡 Result:
In sqlx, if a Transaction goes out of scope without an explicit call to commit or rollback, the transaction is automatically rolled back via its Drop implementation [1][2][3]. This automatic rollback occurs silently in the sense that the Drop trait cannot return a Result, and therefore it cannot propagate errors to the caller [2][3]. Because the rollback is initiated during the synchronous Drop method, sqlx queues a rollback operation using start_rollback, which is intended to be executed upon the next asynchronous invocation of the underlying connection (such as when the connection is returned to a connection pool) [2][3]. Key points regarding this behavior: - Automatic Rollback: If you do not explicitly commit or rollback, sqlx will ensure the transaction is aborted when the Transaction object is dropped [1][3]. - Error Handling: Because the automatic process happens in the drop implementation, any errors that occur during this background rollback cannot be caught or handled by your application code [2][3]. - Implementation: The library performs this by setting a flag in the connection state to trigger a rollback at the next available opportunity [2][3]. It is considered best practice to explicitly call commit on your transactions to ensure they are finalized successfully and to handle potential errors resulting from the commit operation [2][3].
Citations:
Move
rollback_or_logahead of the fallible work.These read-only handlers return early on query/conversion errors, so the explicit rollback/log path is skipped and the transaction falls back to
Drop’s silent rollback. That leaves rollback failures unreported on the error path. Apply the same pattern in the other listed handlers.📍 Affects 5 files
crates/api-core/src/handlers/instance.rs#L281-L282(this comment)crates/api-core/src/handlers/credential.rs#L529-L530crates/api-core/src/handlers/power_shelf.rs#L141-L142crates/api-core/src/handlers/rack.rs#L128-L128crates/api-core/src/handlers/switch.rs#L179-L180🤖 Prompt for AI Agents