Skip to content

Fix 51 smartctlr metrics od there serve #53

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

Merged

Conversation

Owaiseimdad
Copy link
Contributor

@Owaiseimdad Owaiseimdad commented Mar 12, 2025

Sample out put of the api end point /api/v1/metrics/smart.

{
    "data": {
        "available_spare": "100%",
        "available_spare_threshold": "99%",
        "controller_busy_time": "0",
        "critical_warning": "0x00",
        "data_units_read": "513,796,561 [263 TB",
        "data_units_written": "496,894,877 [254 TB",
        "error_information_log_entries": "0",
        "host_read_commands": "3,535,749,949",
        "host_write_commands": "2,633,648,374",
        "media_and_data_integrity_errors": "0",
        "percentage_used": "4%",
        "power_cycles": "241",
        "power_on_hours": "2,332",
        "read_1_entries_from_error_information_log_failed": "GetLogPage failed: system=0x38, sub=0x0, code=745",
        "smart_overall-health_self-assessment_test_result": "PASSED",
        "temperature": "36 Celsius",
        "unsafe_shutdowns": "142"
    },
    "errors": [
        {
            "metric": [
                "Read 1 entries from Error Information Log failed"
            ],
            "err": "Unable to retrieve the 'Read 1 entries from Error Information Log failed'"
        }
    ]
}

Summary by CodeRabbit

  • New Features
    • Introduced a new API endpoint for accessing smart metrics, enhancing system metrics reporting.
    • Users can now benefit from detailed disk health reporting, providing improved insights into system performance and reliability.
    • Added functionality to retrieve and parse SMART metrics from disk devices using the smartctl utility.
    • New data structure for representing SMART metrics has been implemented.

Copy link
Contributor

coderabbitai bot commented Mar 12, 2025

Walkthrough

This pull request introduces a new API endpoint for SMART metrics. A new route (/metrics/smart) is registered in the main application, which connects to a new handler function. This handler retrieves SMART metrics by utilizing functions from a new module that executes the smartctl utility to scan devices and parse SMART data. Additionally, new types and methods are defined to structure the SMART metrics data and its associated errors, thereby expanding the metrics reporting capabilities.

Changes

File Change Summary
cmd/capture/main.go Registered new API V1 route /metrics/smart mapped to the SmartMetrics handler.
internal/handler/metrics.go Added SmartMetrics handler which calls metric.GetSmartMetrics() and responds with JSON data including metrics and errors.
internal/metric/metric.go Introduced new type SmartData and method isMetric() to represent SMART metrics and associated error handling.
internal/metric/smart_metrics.go Added functions (checkSmartctlInstalled, scanDevices, isErrorKey, setField, parseSmartctlOutput, getMetrics, and GetSmartMetrics) to execute smartctl commands, parse output, and aggregate SMART metrics.

Suggested labels

enhancement

Poem

I'm a little rabbit with a hop and a skip,
Coding new routes, giving metrics a flip.
SMART data now dances in lines of delight,
Like carrots in moonlight on a starry night.
With every command, my joy does soar—
Hoppy coding vibes forevermore!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3c9fa7 and e5ec75d.

📒 Files selected for processing (4)
  • cmd/capture/main.go (1 hunks)
  • internal/handler/metrics.go (1 hunks)
  • internal/metric/metric.go (1 hunks)
  • internal/metric/smart_metrics.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/handler/metrics.go
  • cmd/capture/main.go
  • internal/metric/metric.go
  • internal/metric/smart_metrics.go

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Owaiseimdad
Copy link
Contributor Author

@mertssmnoglu have drafted first compatible code, let me know what needs to be renewed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
internal/handler/metrics.go (1)

43-47: Apply goimports for consistent formatting

The file is flagged by static analysis as not goimports-ed. Please run goimports to standardize import ordering and formatting.

🧰 Tools
🪛 golangci-lint (1.62.2)

45-45: File is not goimports-ed

(goimports)

internal/metric/metric.go (1)

23-41: Switch to numeric types for numerical fields

These fields (e.g., temperature, power cycles) might represent numeric data. Storing them as numbers could simplify computations or aggregations.

internal/metric/smart_metrics.go (3)

1-9: Apply goimports for formatting

The file is flagged as not being goimports-ed. Please run goimports to address any formatting or import ordering discrepancies.


11-27: Use %w to wrap errors for better unwrapping

When returning errors with fmt.Errorf, consider using the %w verb to wrap the underlying error, allowing for more seamless error checking and unwrapping later.

🧰 Tools
🪛 golangci-lint (1.62.2)

15-15: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)


117-130: Use errors.As for exit error checks & %w for wrapping

• Use errors.As(err, &exitErr) instead of a direct type assertion to handle any wrapped errors properly.
• Replace %v with %w in fmt.Errorf calls to maintain error context.

🧰 Tools
🪛 golangci-lint (1.62.2)

121-121: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors

(errorlint)


126-126: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e19a589 and 526787f.

📒 Files selected for processing (4)
  • cmd/capture/main.go (1 hunks)
  • internal/handler/metrics.go (1 hunks)
  • internal/metric/metric.go (2 hunks)
  • internal/metric/smart_metrics.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.62.2)
internal/handler/metrics.go

45-45: File is not goimports-ed

(goimports)

internal/metric/metric.go

17-17: File is not goimports-ed

(goimports)

internal/metric/smart_metrics.go

51-51: File is not goimports-ed

(goimports)


15-15: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)


121-121: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors

(errorlint)


126-126: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)

🔇 Additional comments (6)
cmd/capture/main.go (1)

53-54: Route registration looks good

This new endpoint properly extends the metrics API to include SMART data.

internal/metric/metric.go (4)

16-19: Well-structured container for SMART data

The SmartMetric struct effectively encapsulates SMART data alongside any associated errors.

🧰 Tools
🪛 golangci-lint (1.62.2)

17-17: File is not goimports-ed

(goimports)


21-22: Implements the Metric interface

This no-op method ensures SmartMetric fulfills the Metric interface contract.


43-44: Consistent interface implementation

This method is consistent with the existing pattern for implementing Metric.


127-127: Return statement aligns with overall structure

The final return of AllMetrics and the collected errors aligns with the existing patterns in this file.

internal/metric/smart_metrics.go (1)

132-154: Confirm handling multiple devices

Overwriting metrics for each device returns only the last device’s data. If you intend to handle multiple devices, consider aggregating them in a slice or combined structure.

Would you like me to propose a refactor that accumulates metrics for all devices?

Copy link
Member

@mertssmnoglu mertssmnoglu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thanks for this PR! @Owaiseimdad

I manually tested these changes in my PC and it works when I run Capture as root.

  1. Can we check if smartctl command is installed or not and early exit if it's not installed. Like executing command -v smartctl and check if error code is 127 or it's valid command.
  2. I noticed non-standard code style, this can be auto fixed with gofmt -w ./ or just format commands.
  3. Instead of declaring another struct with fields Data and Error, we can use APIResponse. metric module functions should return Metric interface.

Conclusion
This is very good to start to expose smartctl metrics in the happy path. But we should care about error handling to merge these changes. I commented on some lines to improve error handling.

Thank you so much.

@Owaiseimdad
Copy link
Contributor Author

Hey, thanks for this PR! @Owaiseimdad

I manually tested these changes in my PC and it works when I run Capture as root.

  1. Can we check if smartctl command is installed or not and early exit if it's not installed. Like executing command -v smartctl and check if error code is 127 or it's valid command.
  2. I noticed non-standard code style, this can be auto fixed with go fmt -w ./ or just format commands.
  3. Instead of declaring another struct with fields Data and Error, we can use APIResponse. metric module functions should return Metric interface.

Conclusion This is very good to start to expose smartctl metrics in the happy path. But we should care about error handling to merge these changes. I commented on some lines to improve error handling.

Thank you so much.

@mertssmnoglu have made the updates now, also added more readability and maintaince of code in order to make sure that of someone read in future they understand the code.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
internal/metric/metric.go (1)

16-33: Consider using numeric field types for better data manipulation.

All fields in SmartData are stored as strings, which might make further numeric computations or comparisons more cumbersome. Consider changing those fields to numeric types (e.g., int or float64), while properly handling parsing logic. If you only need them as raw strings for display purposes, please feel free to ignore this suggestion.

internal/metric/smart_metrics.go (3)

10-18: Consider using exec.LookPath for a more direct check.

Instead of running "command -v smartctl", using exec.LookPath("smartctl") can be a more idiomatic way in Go to check if smartctl is in the system's PATH.


43-74: Consider handling or logging unexpected keys in the output.

Right now, any key not included in these switch cases is ignored. If smartctl adds new metrics in the future, those might go unnoticed. Logging them could be helpful.


76-130: Parsing approach may be brittle against future format changes.

Manually parsing lines could break if smartctl output changes significantly. Consider a more robust strategy or keep this well-documented and tested against multiple versions of smartctl.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 526787f and 0d312b7.

📒 Files selected for processing (3)
  • internal/handler/metrics.go (1 hunks)
  • internal/metric/metric.go (2 hunks)
  • internal/metric/smart_metrics.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/handler/metrics.go
🔇 Additional comments (6)
internal/metric/metric.go (1)

33-33: No-op method is acceptable.

isMetric() is a marker method to satisfy the Metric interface. No concerns here.

internal/metric/smart_metrics.go (5)

1-9: Good separation of the new metrics logic into its own package.

No issues with the package declaration and imports.


20-36: Device scanning logic looks sound.

The function accurately captures device paths returned by smartctl --scan. No immediate issues identified.


38-41: Case-insensitive check for error keywords is appropriate.

This logic properly detects "failed" or "error" in any case variant.


132-154: Confirm ignoring exit code 4 is appropriate.

According to smartctl docs, exit code 4 might be non-critical, but please verify this behavior aligns with your desired workflow. If it's truly safe, a clarifying comment may help future maintainers.


156-194: Returning only the last device's SMART data.

This function overwrites metrics on each loop iteration, ultimately returning data for only the final device. If you need to collect metrics for all devices, consider storing them in a slice or mapping them by device name.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
internal/metric/metric.go (1)

1-1: ⚠️ Potential issue

Fix formatting issues flagged by pipeline.

The pipeline failure indicates formatting issues with this file. Please run go fmt ./... or goimports -w internal/metric/metric.go to fix these issues.

#!/bin/bash
# Show what the formatting changes would be
goimports -d internal/metric/metric.go
🧰 Tools
🪛 GitHub Actions: lint

[error] 1-1: File is not properly formatted (goimports)

🧹 Nitpick comments (2)
internal/metric/metric.go (2)

16-31: Add missing SMART metrics fields according to PR objectives.

The SmartData struct is well-structured with appropriate JSON tags. However, it's missing some fields that were mentioned in the PR objectives, notably:

  • error_information_log_entries
  • media_and_data_integrity_errors

These should be added to ensure comprehensive SMART data collection.

Also, the JSON tag for SmartOverallHealthResult uses hyphens (smart_overall-health_self-assessment_test_result), which is inconsistent with the underscore format used in other fields.

-	SmartOverallHealthResult      string `json:"smart_overall-health_self-assessment_test_result"`
+	SmartOverallHealthResult      string `json:"smart_overall_health_self_assessment_test_result"`
+	ErrorInformationLogEntries    string `json:"error_information_log_entries"`
+	MediaAndDataIntegrityErrors   string `json:"media_and_data_integrity_errors"`
🧰 Tools
🪛 golangci-lint (1.64.8)

17-17: File is not properly formatted

(goimports)


16-31: Consider using appropriate data types for numeric metrics.

Currently, all fields in the SmartData struct are defined as strings. For metrics that are naturally numeric (like PercentageUsed, PowerCycles, etc.), consider using appropriate numeric types (int, uint64, float64) to facilitate data processing and validation.

For example:

-	PowerCycles                   string `json:"power_cycles"`
-	PowerOnHours                  string `json:"power_on_hours"`
-	PercentageUsed                string `json:"percentage_used"`
+	PowerCycles                   uint64 `json:"power_cycles"`
+	PowerOnHours                  uint64 `json:"power_on_hours"`
+	PercentageUsed                float64 `json:"percentage_used"`

This would require proper parsing of the values from the smartctl output, but it would make the data more usable for calculations and comparisons.

🧰 Tools
🪛 golangci-lint (1.64.8)

17-17: File is not properly formatted

(goimports)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d312b7 and caa798d.

📒 Files selected for processing (1)
  • internal/metric/metric.go (2 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
internal/metric/metric.go

17-17: File is not properly formatted

(goimports)

🪛 GitHub Actions: lint
internal/metric/metric.go

[error] 1-1: File is not properly formatted (goimports)

🔇 Additional comments (2)
internal/metric/metric.go (2)

33-33: LGTM: Empty implementation of isMetric() satisfies the Metric interface.

This implementation is sufficient to make SmartData satisfy the Metric interface defined on line 7-9.


16-31: Complete implementation based on previous review feedback.

The previous review comments suggest that you've correctly removed the redundant SmartMetric struct in favor of using SmartData directly in the metric module, which aligns with best practices.

🧰 Tools
🪛 golangci-lint (1.64.8)

17-17: File is not properly formatted

(goimports)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
internal/metric/smart_metrics.go (2)

42-73: Consider adding validation for unrecognized keys

The setField function handles known SMART metrics by mapping them to appropriate fields in the SmartData struct, but it silently ignores unrecognized keys. Consider logging unrecognized keys for debugging purposes or returning a boolean to indicate whether the field was set.

func setField(key, value string, data *SmartData) {
+   originalKey := key
    switch strings.ToLower(key) {
    case "available spare":
        data.AvailableSpare = value
    // ... other cases ...
    case "unsafe shutdowns":
        data.UnsafeShutdowns = value
+   default:
+       // Optionally log unrecognized keys for debugging
+       // log.Printf("Unrecognized SMART metric key: %s", originalKey)
    }
}

75-129: Enhance error handling for invalid smart data sections

The parsing logic is generally good, but there are a few edge cases to consider:

  1. When startMarker is found but the section is malformed (no second "===" marker), the endIdx check may not correctly limit the section.
  2. You're appending errors for any keys containing "error" or "failed" without checking if the value actually indicates an error.

Consider refining these conditions for more accurate error reporting.

// Extract the section starting from the marker
section := output[startIdx:]
endIdx := strings.Index(section, "===")
-if endIdx > len(startMarker) {
+if endIdx > 0 && endIdx > len(startMarker) {
    section = section[:endIdx]
}

// ...

// If the key contains "error" or "failed", add it to the errors
if isErrorKey(key) {
+   // Only report as error if the value indicates an issue
+   if strings.Contains(strings.ToLower(value), "failed") || strings.Contains(strings.ToLower(value), "error") {
        errors = append(errors, CustomErr{
            Metric: []string{key},
            Error:  fmt.Sprintf("Unable to retrieve the '%s'", key),
        })
+   }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caa798d and f79ef67.

📒 Files selected for processing (1)
  • internal/metric/smart_metrics.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
internal/metric/smart_metrics.go (1)
internal/metric/metric.go (4) (4)
  • SmartData (16-31)
  • CustomErr (44-47)
  • errors (97-97)
  • Metric (7-9)
🪛 golangci-lint (1.64.8)
internal/metric/smart_metrics.go

23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)


136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors

(errorlint)


159-159: File is not properly formatted

(goimports)

🪛 GitHub Actions: lint
internal/metric/smart_metrics.go

[error] 23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[error] 136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors (errorlint)


[error] 159-159: File is not properly formatted (goimports)

🔇 Additional comments (3)
internal/metric/smart_metrics.go (3)

10-17: Function successfully handles checking for smartctl

The checkSmartCtlInstalled function properly uses exec.LookPath to determine if the smartctl utility is available in the system path, which addresses the requirement mentioned in the PR comments.


37-40: LGTM! Case-insensitive error key detection implemented

The function correctly implements case-insensitive checks for error indicators by converting the key to lowercase before checking for "failed" or "error" substrings, which addresses the previous review comment.


163-201:

❓ Verification inconclusive

Address potential overwriting of metrics from multiple devices

In the current implementation, if multiple devices are found, the metrics from the last device processed will overwrite all previous metrics. This logic may lose valuable information from other devices:

// This will overwrite any previously collected metrics
if metric != nil {
    metrics = *metric
}

Consider modifying the approach to either:

  1. Aggregate metrics by device (return a map of device -> metrics)
  2. Combine metrics with clear labeling to indicate which device they came from
func GetSmartMetrics() (map[string]SmartData, []CustomErr) {
-   var metrics SmartData
+   metrics := make(map[string]SmartData)
    var smartCtlrErrs []CustomErr

    // ... existing code ...

    // Iterate over devices and collect metrics
    for _, device := range devices {
        metric, metricErr := getMetrics(device)

        if metric != nil {
-           metrics = *metric
+           metrics[device] = *metric
        }

        if len(metricErr) > 0 {
-           smartCtlrErrs = append(smartCtlrErrs, metricErr...)
+           // Add device information to each error
+           for i := range metricErr {
+               metricErr[i].Metric = append([]string{"smart: " + device}, metricErr[i].Metric...)
+           }
+           smartCtlrErrs = append(smartCtlrErrs, metricErr...)
        }
    }

    return metrics, smartCtlrErrs
}

Alternatively, if the current API design requires a single SmartData object to be returned, consider adding additional fields to the SmartData struct to indicate which metrics came from which devices.


🏁 Script executed:

#!/bin/bash
# Check how the metrics API endpoint is used elsewhere in the codebase
# to understand if changing the return type would break existing code

# Look for references to GetSmartMetrics
rg -A 2 -B 2 "GetSmartMetrics" --type go

Length of output: 707


Revisit Metrics Aggregation for Multiple Devices

The implementation currently overwrites metrics from each device, meaning only the last device’s metrics are returned. Note that downstream code (e.g., in internal/handler/metrics.go, which calls GetSmartMetrics expecting a single SmartData value) may be impacted if the API contract is changed. You have two potential directions:

  • Aggregate Metrics Per Device:
    Change the return type from a single SmartData to something like a map (or slice) mapping each device to its metrics. For example:

    func GetSmartMetrics() (map[string]SmartData, []CustomErr) {
    -   var metrics SmartData
    +   metrics := make(map[string]SmartData)
        var smartCtlrErrs []CustomErr
    
        // … existing code …
    
        for _, device := range devices {
            metric, metricErr := getMetrics(device)
    
            if metric != nil {
    -           metrics = *metric
    +           metrics[device] = *metric
            }
    
            if len(metricErr) > 0 {
    -           smartCtlrErrs = append(smartCtlrErrs, metricErr...)
    +           // Prefix error metrics with device information
    +           for i := range metricErr {
    +               metricErr[i].Metric = append([]string{"smart: " + device}, metricErr[i].Metric...)
    +           }
    +           smartCtlrErrs = append(smartCtlrErrs, metricErr...)
            }
        }
    
        return metrics, smartCtlrErrs
    }

    Caveat: This change would also require updates in all consumers (for instance, in internal/handler/metrics.go) that currently expect a SmartData object.

  • Enhance the Existing API:
    If the API design must remain unchanged, consider extending the SmartData structure to support aggregation internally (for example, by adding fields that identify the source device). This way, you can combine metrics from multiple devices without altering the return type.

Please verify which approach fits your design goals and ensure any modifications are propagated to dependent parts of the codebase.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
internal/metric/smart_metrics.go (2)

23-23: ⚠️ Potential issue

Use error wrapping with %w instead of %v

When using fmt.Errorf, use the %w verb instead of %v to properly wrap the original error. This allows calling code to use errors.Unwrap() or errors.Is() to inspect and handle the underlying error.

- return nil, fmt.Errorf("failed to scan devices: %v", err)
+ return nil, fmt.Errorf("failed to scan devices: %w", err)
🧰 Tools
🪛 golangci-lint (1.64.8)

23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)

🪛 GitHub Actions: lint

[error] 23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


136-150: ⚠️ Potential issue

Use errors.As instead of type assertion for error checking

Type assertions on errors will fail for wrapped errors. Use errors.As to check for specific error types, as this properly handles wrapped errors.

+import "errors"

// ...

// If there's an exit error with exit code 4, we ignore the error
-if exitErr, ok := err.(*exec.ExitError); ok {
+var exitErr *exec.ExitError
+if errors.As(err, &exitErr) {
    switch exitErr.ExitCode() {
    // ... rest of the code remains the same
🧰 Tools
🪛 golangci-lint (1.64.8)

136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors

(errorlint)

🪛 GitHub Actions: lint

[error] 136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors (errorlint)

🧹 Nitpick comments (2)
internal/metric/smart_metrics.go (2)

75-129: Consider optimizing the regular expression compilation

The regex at line 113 is compiled each time the function is called. For better performance, consider defining it as a package-level variable that's compiled once.

+// Compile the regex once at package level
+var spaceRegex = regexp.MustCompile(`\s+`)

 func parseSmartctlOutput(output string) (*SmartData, []CustomErr) {
     // ...

     // Clean up value by removing extra spaces and brackets
-    value = regexp.MustCompile(`\s+`).ReplaceAllString(value, " ")
+    value = spaceRegex.ReplaceAllString(value, " ")

42-73: Consider handling unknown metrics

The setField function only handles specifically defined metrics. You might want to consider adding a default case to log or handle unknown metrics for future extensibility.

func setField(key, value string, data *SmartData) {
    switch strings.ToLower(key) {
    // existing cases...
+   default:
+       // Optional: Log unknown metrics or store them in a map of additional metrics
+       // log.Printf("Unknown SMART metric: %s with value: %s", key, value)
    }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f79ef67 and e3c9fa7.

📒 Files selected for processing (1)
  • internal/metric/smart_metrics.go (1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.64.8)
internal/metric/smart_metrics.go

23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors

(errorlint)


136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors

(errorlint)

🪛 GitHub Actions: lint
internal/metric/smart_metrics.go

[error] 23-23: non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[error] 136-136: type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors (errorlint)

🔇 Additional comments (3)
internal/metric/smart_metrics.go (3)

168-175: LGTM! Good error handling for smartctl installation check

The code properly checks if smartctl is installed before attempting to use it, and returns a clear error message if it's not available.


37-40: Good implementation of case-insensitive error detection

The isErrorKey function correctly implements case-insensitive checks for error indicators by converting the key to lowercase before checking, which addresses a previous review comment.


187-198:

❓ Verification inconclusive

Review metric aggregation logic

Currently, each device's metrics overwrite the previous device's metrics (line 192). If you intend to aggregate metrics from all devices, you need to adjust this logic to combine metrics instead of overwriting them.


🏁 Script executed:

#!/bin/bash
# Check if there are multiple NVMe devices that would be detected by smartctl scan
smartctl --scan | grep -c nvme

Length of output: 133


Action Required: Revise Metrics Aggregation Logic and Verify NVMe Presence

  • The current loop in internal/metric/smart_metrics.go (lines 187–198) assigns *metric to metrics for each device, effectively overwriting previously collected metrics rather than aggregating them.
  • If the intent is to combine metrics from all devices, you should update this logic (for example, by appending or merging the metrics).
  • Note: The smartctl --scan command did not produce useful output (possibly due to smartctl not being installed). Please manually verify whether multiple NVMe devices are present in your target environment, as this impacts whether aggregation is necessary.

Copy link
Member

@mertssmnoglu mertssmnoglu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed the linting issues for you so it can pass the CI/lint now. I tested it manually and works as expected.
Thanks for your efforts, this is really valuable.

@mertssmnoglu mertssmnoglu added the enhancement New feature or request label Mar 25, 2025
@Owaiseimdad
Copy link
Contributor Author

@mertssmnoglu thanks so much for this ticket learned alot, apologies for lot of errors. Will make sure to take care of them next time. Let me know if you have anything else as well.

@mertssmnoglu mertssmnoglu merged commit aadedfb into bluewave-labs:develop Mar 25, 2025
2 checks passed
@mertssmnoglu
Copy link
Member

@mertssmnoglu thanks so much for this ticket learned alot, apologies for lot of errors. Will make sure to take care of them next time. Let me know if you have anything else as well.

I apologize for late(5 days) respond. @Owaiseimdad

No problem, thank you so much. Merged 💯

@mertssmnoglu mertssmnoglu linked an issue Mar 25, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Metrics - Disk Health with S.M.A.R.T.
2 participants