Skip to content

using zap logger #22

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
merged 3 commits into from
Dec 2, 2024
Merged

using zap logger #22

merged 3 commits into from
Dec 2, 2024

Conversation

lvjing2
Copy link
Contributor

@lvjing2 lvjing2 commented Nov 26, 2024

old logger format
image

new logger format

image

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new logging framework (zaplogger) for improved logging consistency across various modules.
    • Enhanced context management in HttpTunnel and MqttTunnel implementations.
  • Bug Fixes

    • Updated error handling to ensure more structured logging and clarity in error reporting.
  • Documentation

    • Updated import paths for logging libraries to reflect the transition to the new logging framework.
  • Refactor

    • Streamlined method signatures to reduce context passing, utilizing struct fields instead.
    • Added a new field LatestStateRecord to the ArkSimpleBizInfoData struct for storing the latest state record.

These updates enhance the overall logging mechanisms and improve the clarity of operations within the application.

Copy link

coderabbitai bot commented Nov 26, 2024

Walkthrough

The pull request introduces significant changes to the logging and error handling mechanisms across multiple files in the codebase. The primary focus is on replacing the logr and logrus libraries with a custom zaplogger. This transition involves updating function signatures to include context parameters, modifying error logging practices, and ensuring consistent logging throughout various components, including module controllers, tunnels, and utility functions. Additionally, the import paths for the logging library have been updated to reflect the new structure.

Changes

File Path Change Summary
cmd/module-controller/main.go Replaced logr with zaplogger, updated error logging calls, modified function signatures of NewMqttTunnel and NewHttpTunnel to include ctx.
cmd/self-test/main.go Updated import path for logging package.
common/utils/utils.go Transitioned from logrus to zaplogger, updated logging statements in multiple functions, and changed severity levels for some logs.
common/zaplogger/logger.go Introduced a new logging utility based on zap, with functions for logger retrieval and context management.
controller/module_deployment_controller/module_deployment_controller.go Replaced logrus with zaplogger, updated logging statements, and modified QueryContainerBaseline method to include ctx.
go.mod Updated version of github.com/koupleless/virtual-kubelet and added github.com/virtual-kubelet/virtual-kubelet as a new dependency.
module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go Replaced logging with zaplogger, updated error logging in service methods.
module_tunnels/koupleless_http_tunnel/http_tunnel.go Added ctx field to HttpTunnel, modified method signatures to use the struct's ctx, and updated logging to use zaplogger.
module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go Transitioned from logrus to zaplogger for logging within MQTT client, updating logging calls throughout the methods.
module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go Added ctx field to MqttTunnel, modified method signatures, and updated logging to use zaplogger.
report_server/server.go Updated import for zaplogger, modified error logging in InitReportServer.
suite/http/mock_http_base.go Updated logging import path and refined the Start method's exit channel handling.
suite/http/suite_test.go Updated logging imports and modified HttpTunnel instantiation to include ctx.
suite/mqtt/mock_mqtt_base.go Changed logging import path without altering method functionality.
suite/mqtt/module_mqtt_lifecycle_test.go Updated logging import path with no changes to test logic.
suite/mqtt/suite_test.go Updated logging imports and modified mqttTunnel instantiation to include ctx.

Possibly related PRs

  • simple the data transformer and logic #20: The changes in common/model/model.go regarding the restructuring of ArkSimpleBizInfoData to a more detailed struct format relate to the overall improvements in data handling and logging in the main PR, which also focuses on enhancing clarity and structure in logging and error handling.

🐰 "In the code where rabbits hop,
Logging now won't ever stop!
With zaplogger leading the way,
Errors logged in bright array.
Contexts shared, no need to fret,
A clearer path, we won't forget!" 🐇


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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 resolve resolve all the CodeRabbit review comments.
  • @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.

@lvjing2 lvjing2 requested a review from gaosaroma November 26, 2024 09:00
Copy link

@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: 17

🧹 Outside diff range and nitpick comments (28)
common/zaplogger/logger.go (5)

1-7: Add package documentation

Consider adding a package documentation comment that describes the purpose and usage of this logging utility package.

+// Package zaplogger provides a centralized logging utility based on zap logger.
+// It manages a global logger instance and provides context-aware logging capabilities.
 package zaplogger

9-9: Consider making the logger configuration environment-aware

The current implementation hardcodes Development: true, which enables development mode logging. This might not be suitable for production environments as it can impact performance and log verbosity.

Consider:

  1. Making the configuration configurable through environment variables or flags
  2. Adding different preset configurations for development and production environments
  3. Making the logger variable private and providing thread-safe initialization

Example implementation:

var (
    defaultOpts = &zap.Options{
        Development: false, // Default to production mode
    }
    loggerInstance atomic.Value
)

func init() {
    // Read from environment or flags
    if os.Getenv("LOG_DEV_MODE") == "true" {
        defaultOpts.Development = true
    }
    loggerInstance.Store(zap.New(zap.UseFlagOptions(defaultOpts)))
}

11-17: Add function documentation

The implementation looks good, but consider adding godoc comments for better API documentation.

+// GetLogger returns the global logger instance
 func GetLogger() logr.Logger {
     return logger
 }

+// WithLogger returns a new context with the provided logger
+// It's safe to pass a nil logger, in which case a no-op logger will be used
 func WithLogger(ctx context.Context, logger logr.Logger) context.Context {
     return logr.NewContext(ctx, logger)
 }

19-26: Enhance error handling and add context validation

While the implementation provides good fallback behavior, it could be improved with better error handling and context validation.

Consider this enhanced implementation:

 func FromContext(ctx context.Context) logr.Logger {
+    if ctx == nil {
+        return logger
+    }
     l, err := logr.FromContext(ctx)
     if err != nil {
+        // Log the reason for falling back to default logger
+        logger.V(1).Info("using default logger", "reason", err.Error())
         return logger
     }
     return l
 }

Also, consider adding documentation:

+// FromContext extracts a logger from the given context.
+// If the context is nil or doesn't contain a logger,
+// it returns the default global logger.
 func FromContext(ctx context.Context) logr.Logger {

1-26: Consider adding log sanitization and level configuration

As this logger will be used throughout the application, consider implementing:

  1. Log level configuration to control verbosity in different environments
  2. Log sanitization helpers to prevent sensitive data exposure
  3. Structured logging guidelines to ensure consistent log format

Consider adding helper functions for common logging patterns:

// Example helper functions
func WithSanitized(key string, value interface{}) logr.Logger {
    // Implement sanitization logic
    return logger.WithValues(key, sanitize(value))
}

func sanitize(value interface{}) interface{} {
    // Implement sanitization logic for sensitive data
    // e.g., email addresses, tokens, etc.
}
module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go (2)

22-41: Consider adding debug/info level logs for successful operations

While error logging is well implemented, adding debug or info level logs for successful operations would improve observability. Consider adding:

  • Debug log before making the HTTP request
  • Info log on successful installation
 func (h *Service) InstallBiz(ctx context.Context, req InstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
 	logger := zaplogger.FromContext(ctx)
+	logger.Debug("initiating biz installation", "baseIP", baseIP, "arkletPort", arkletPort)
 	
 	resp, err := h.client.R().
 		SetContext(ctx).
 		SetBody(req).
 		Post(fmt.Sprintf("http://%s:%d/installBiz", baseIP, arkletPort))
 	
 	// ... error handling ...
 	
+	logger.Info("successfully installed biz", "baseIP", baseIP)
 	return response, nil

41-41: Standardize error message formatting

Error messages have inconsistent formatting. Consider standardizing:

  • Capitalize method names consistently
  • Use consistent message structure
-		logger.Error(err, fmt.Sprintf("Unmarshal InstallBiz response: %s", resp.Body()))
+		logger.Error(err, fmt.Sprintf("failed to unmarshal install_biz response: %s", resp.Body()))

-		logger.Error(err, fmt.Sprintf("Unmarshal UnInstallBiz response: %s", resp.Body()))
+		logger.Error(err, fmt.Sprintf("failed to unmarshal uninstall_biz response: %s", resp.Body()))

-		logger.Error(err, fmt.Sprintf("Unmarshal QueryAllBiz response: %s", resp.Body()))
+		logger.Error(err, fmt.Sprintf("failed to unmarshal query_all_biz response: %s", resp.Body()))

-		logger.Error(err, fmt.Sprintf("Unmarshal Health response: %s", resp.Body()))
+		logger.Error(err, fmt.Sprintf("failed to unmarshal health response: %s", resp.Body()))

Also applies to: 68-68, 95-95, 122-122

report_server/server.go (3)

Line range hint 82-84: Standardize logging approach across the file

The file uses a mix of standard log package and zaplogger, which is inconsistent with the PR's objective of transitioning to zaplogger.

Replace standard log.Println calls with zaplogger:

-    log.Println("Error sending to webhook:", err.Error())
+    zaplogger.GetLogger().Error(err, "Error sending to webhook")

-    log.Println("Starting server on :8080")
+    zaplogger.GetLogger().Info("Starting server", "port", ":8080")

Also applies to: 112-112

🧰 Tools
🪛 golangci-lint (1.62.2)

8-8: SA1019: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package [io] or package [os], and those implementations should be preferred in new code. See the specific function documentation for details.

(staticcheck)


Line range hint 106-113: Consider adding graceful shutdown

The server initialization lacks graceful shutdown handling, which could lead to connection interruptions during deployments or shutdowns.

Consider:

  1. Accepting a context parameter in InitReportServer
  2. Using http.Server with Shutdown method
  3. Implementing signal handling for graceful termination

Would you like me to provide an example implementation?


Line range hint 106-113: Add configuration validation

The server starts without validating if any webhooks are configured. This could lead to a running server that can't fulfill its primary purpose.

Consider adding validation:

 func InitReportServer() {
     reportHooks := utils.GetEnv("REPORT_HOOKS", "")
     webhooks = strings.Split(reportHooks, ";")
+    
+    if len(webhooks) == 0 || (len(webhooks) == 1 && webhooks[0] == "") {
+        zaplogger.GetLogger().Warn("No webhooks configured, server will not forward any logs")
+    }
 
     http.HandleFunc("/log", logHandler)
suite/http/suite_test.go (1)

Line range hint 88-94: Consider revising the error handling approach.

While the error handling is comprehensive, consider these improvements:

  1. In a test suite, failing the test might be more appropriate than panicking
  2. The error message duplicates the tunnel key information

Consider this alternative implementation:

  err = httpTunnel.Start(clientID, env)
  if err != nil {
    log.G(ctx).WithError(err).Error("failed to start tunnel", httpTunnel.Key())
-   panic(fmt.Sprintf("failed to start tunnel %s", httpTunnel.Key()))
+   Fail(fmt.Sprintf("failed to start tunnel: %v", err))
  } else {
    log.G(ctx).Info("Tunnel started: ", httpTunnel.Key())
  }
module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go (2)

56-64: Standardize logging approach across handlers

While the transition to zaplogger is correct, the logging approach is inconsistent:

  • Line 56: Uses fmt.Sprintf
  • Line 60: Uses direct string
  • Line 64: Uses fmt.Sprintf

Consider standardizing to use structured logging for better performance and consistency:

-	zaplogger.GetLogger().Info(fmt.Sprintf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic()))
+	zaplogger.GetLogger().Info("Received message",
+		"payload", msg.Payload(),
+		"topic", msg.Topic())

-	zaplogger.GetLogger().Info("Connected")
+	zaplogger.GetLogger().Info("Connected")  // This is fine as is

-	zaplogger.GetLogger().Info(fmt.Sprintf("Connection lost %v\n", err))
+	zaplogger.GetLogger().Info("Connection lost",
+		"error", err)

163-171: Standardize logging approach in subscription methods

Similar to the handlers, consider using structured logging for consistency and better performance:

-	zaplogger.GetLogger().Info(fmt.Sprintf("Subscribing to topic: %s\n", topic))
+	zaplogger.GetLogger().Info("Subscribing to topic",
+		"topic", topic)

-	zaplogger.GetLogger().Info(fmt.Sprintf("Unsubscribing from topic: %s\n", topic))
+	zaplogger.GetLogger().Info("Unsubscribing from topic",
+		"topic", topic)
suite/http/mock_http_base.go (3)

Line range hint 91-91: Standardize logging patterns across the codebase

The code currently mixes different logging approaches:

  1. Context-aware logging with zap: log.G(ctx).Info()
  2. Global logging with logrus: logrus.Errorf()

This inconsistency makes it harder to maintain and monitor logs effectively.

Standardize all logging calls to use the context-aware zap logger. Here's how to refactor the logging calls:

// In error handling for request body
-logrus.Errorf("error reading body: %s", err)
+log.G(ctx).Error("error reading body", "error", err)

// In heartbeat error handling
-logrus.Errorf("error calling heartbeat: %s", err)
+log.G(ctx).Error("error calling heartbeat", "error", err)

// In biz installation/uninstallation
-logrus.Infof("install biz %s from http base", identity)
+log.G(ctx).Info("installing biz from http base", "identity", identity)

-logrus.Infof("uninstall biz %s from http base", identity)
+log.G(ctx).Info("uninstalling biz from http base", "identity", identity)

Note: This requires propagating the context parameter to the processInstallBiz and processUnInstallBiz methods.

Also applies to: 111-111, 127-127, 130-130, 139-139, 156-156, 214-214, 238-238


Line range hint 83-117: Consider architectural improvements for context propagation

The current HTTP handler implementation makes it challenging to use context-aware logging consistently. The handlers and business processing methods (processInstallBiz, processUnInstallBiz) don't have access to the request context.

Consider these improvements:

  1. Update method signatures to accept context:
-func (b *MockHttpBase) processInstallBiz(msg []byte) []byte {
+func (b *MockHttpBase) processInstallBiz(ctx context.Context, msg []byte) []byte {

-func (b *MockHttpBase) processUnInstallBiz(msg []byte) []byte {
+func (b *MockHttpBase) processUnInstallBiz(ctx context.Context, msg []byte) []byte {
  1. Pass request context in HTTP handlers:
 mux.HandleFunc("/installBiz", func(w http.ResponseWriter, r *http.Request) {
     if b.reachable {
         w.WriteHeader(http.StatusOK)
         w.Header().Set("Content-Type", "application/json")

         defer r.Body.Close()
         body, err := io.ReadAll(r.Body)
         if err != nil {
-            logrus.Errorf("error reading body: %s", err)
+            log.G(r.Context()).Error("error reading body", "error", err)
             return
         }

-        w.Write(b.processInstallBiz(body))
+        w.Write(b.processInstallBiz(r.Context(), body))
     } else {
         w.WriteHeader(http.StatusBadGateway)
     }
 })

Also applies to: 204-248


Line range hint 83-117: Improve error handling in HTTP handlers

While not directly related to the logging transition, consider these improvements:

  1. Check for errors when writing responses
  2. Use proper error response structures

Example improvement:

 mux.HandleFunc("/installBiz", func(w http.ResponseWriter, r *http.Request) {
     if b.reachable {
-        w.WriteHeader(http.StatusOK)
-        w.Header().Set("Content-Type", "application/json")
+        w.Header().Set("Content-Type", "application/json")
 
         defer r.Body.Close()
         body, err := io.ReadAll(r.Body)
         if err != nil {
-            logrus.Errorf("error reading body: %s", err)
+            w.WriteHeader(http.StatusBadRequest)
+            json.NewEncoder(w).Encode(map[string]string{"error": "failed to read request body"})
             return
         }
 
-        w.Write(b.processInstallBiz(body))
+        w.WriteHeader(http.StatusOK)
+        if err := json.NewEncoder(w).Encode(b.processInstallBiz(body)); err != nil {
+            log.G(r.Context()).Error("failed to write response", "error", err)
+        }
     } else {
         w.WriteHeader(http.StatusBadGateway)
     }
 })
cmd/module-controller/main.go (4)

21-21: Consider cleaning up logging-related imports

The code still maintains imports for both the old logging system (logrus, logruslogger) and the new zaplogger. Consider removing the unused logging imports after completing the migration to zaplogger.

Also applies to: 43-43, 44-44


103-104: Address TODO and consider parameterizing QPS/Burst values

The QPS and Burst values are hardcoded. Consider:

  1. Moving these values to configuration
  2. Making them configurable via environment variables, similar to other parameters

Example:

-kubeConfig.QPS = 100
-kubeConfig.Burst = 200
+kubeConfig.QPS = float32(utils.GetEnvAsInt("KUBE_QPS", 100))
+kubeConfig.Burst = utils.GetEnvAsInt("KUBE_BURST", 200)

Would you like me to help implement this change or create an issue to track this improvement?


Line range hint 190-192: Migrate remaining logrus error handling to zaplogger

There's still an instance of old logging pattern. Consider updating to zaplogger:

-log.G(ctx).WithError(err).Error("failed to parse HTTP_TUNNEL_LISTEN_PORT, set default port 7777")
+zlogger.Error(err, "failed to parse HTTP_TUNNEL_LISTEN_PORT, set default port 7777")

Line range hint 159-166: Complete logger migration for remaining error handlers

There are still instances of the old logging pattern. Consider updating these to use zaplogger:

 if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
-    log.G(ctx).Error(err, "unable to set up health check")
+    zlogger.Error(err, "unable to set up health check")
     os.Exit(1)
 }
 if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
-    log.G(ctx).Error(err, "unable to set up ready check")
+    zlogger.Error(err, "unable to set up ready check")
     os.Exit(1)
 }

-log.G(ctx).Info("Module controller running")
+zlogger.Info("Module controller running")
 err = mgr.Start(signals.SetupSignalHandler())
 if err != nil {
-    log.G(ctx).WithError(err).Error("failed to start manager")
+    zlogger.Error(err, "failed to start manager")
 }

Also applies to: 171-173

common/utils/utils.go (2)

189-189: Improve logging efficiency and debug-ability

Consider these improvements:

  1. Use structured logging instead of string formatting
  2. Consider using debug level for parsing errors
  3. Add context fields for better debugging
-zaplogger.GetLogger().Info(fmt.Sprintf("failed to parse change time %s", record.ChangeTime))
+zaplogger.GetLogger().Debug("Failed to parse change time", 
+    "changeTime", record.ChangeTime,
+    "state", state)

206-217: LGTM: Good structured logging implementation with minor suggestions

The logging implementation follows good practices with structured fields and appropriate log levels. Consider these minor enhancements:

  1. Add more context fields like cluster name
  2. Make error messages more descriptive
-logger.Error(deleteErr, "delete base node failed")
+logger.Error(deleteErr, "Failed to delete base node from API server")
module_tunnels/koupleless_http_tunnel/http_tunnel.go (2)

176-179: Handle Write errors in HTTP handlers

The error return value from w.Write is not being checked. While write errors in HTTP handlers are often not critical (as the connection might be already broken), it's good practice to log them for debugging purposes.

-			w.Write([]byte(err.Error()))
+			if _, writeErr := w.Write([]byte(err.Error())); writeErr != nil {
+				logger.Error(writeErr, "failed to write error response")
+			}
🧰 Tools
🪛 golangci-lint (1.62.2)

178-178: Error return value of w.Write is not checked

(errcheck)


Line range hint 264-267: Improve error handling pattern

Consider defining error variables for common errors and providing more descriptive error messages. This would improve error handling consistency and debugging experience.

+var (
+	ErrNetworkInfoNotFound = errors.New("base network info not found for node")
+)

-		return errors.New("network info not found")
+		return fmt.Errorf("%w: nodeID=%s", ErrNetworkInfoNotFound, nodeID)

Also applies to: 299-302

controller/module_deployment_controller/module_deployment_controller.go (3)

222-227: Consider enhancing the error message

While the logging is implemented correctly, the error message could be more descriptive to aid in troubleshooting.

Consider this enhancement:

-		logger.Info(fmt.Sprintf("failed to get cluster name of node %s", node.Name))
+		logger.Info(fmt.Sprintf("failed to get cluster name of node %s: cluster label not found", node.Name))

299-299: Fix typo in error message

There's a typo in the error message ("relicas" instead of "replicas").

Apply this fix:

-			logger.Error(err, fmt.Sprintf("failed to get nodes of cluster %s, skip to update relicas", clusterName))
+			logger.Error(err, fmt.Sprintf("failed to get nodes of cluster %s, skip to update replicas", clusterName))

Line range hint 1-391: Consider standardizing error handling patterns

While the logging implementation is good, consider standardizing error handling patterns across the controller. Some suggestions:

  • Define standard error message templates for common operations
  • Consider implementing structured logging fields consistently
  • Consider adding log level guidelines for different types of events (Info vs. Error)
module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (1)

Line range hint 179-188: Avoid using shared context in anonymous functions

In the queryBaselineMsgCallback, the anonymous function should receive the context explicitly to prevent unintended behaviors, especially if the context may change or be canceled.

Pass ctx to the goroutine:

-go func() {
+go func(ctx context.Context) {
 	// function body...
-}()
+}(ctx)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9731d90 and f5f1325.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • cmd/module-controller/main.go (8 hunks)
  • cmd/self-test/main.go (1 hunks)
  • common/utils/utils.go (4 hunks)
  • common/zaplogger/logger.go (1 hunks)
  • controller/module_deployment_controller/module_deployment_controller.go (14 hunks)
  • go.mod (1 hunks)
  • module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go (2 hunks)
  • module_tunnels/koupleless_http_tunnel/http_tunnel.go (22 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go (3 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (15 hunks)
  • report_server/server.go (2 hunks)
  • suite/http/mock_http_base.go (1 hunks)
  • suite/http/suite_test.go (2 hunks)
  • suite/mqtt/mock_mqtt_base.go (1 hunks)
  • suite/mqtt/module_mqtt_lifecycle_test.go (1 hunks)
  • suite/mqtt/suite_test.go (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • cmd/self-test/main.go
  • suite/mqtt/mock_mqtt_base.go
  • suite/mqtt/module_mqtt_lifecycle_test.go
🧰 Additional context used
🪛 GitHub Check: unit-test
controller/module_deployment_controller/module_deployment_controller.go

[failure] 198-198:
fmt.Sprintf format % is missing verb at end of string

🪛 golangci-lint (1.62.2)
controller/module_deployment_controller/module_deployment_controller.go

198-198: printf: fmt.Sprintf format % is missing verb at end of string

(govet)

module_tunnels/koupleless_http_tunnel/http_tunnel.go

178-178: Error return value of w.Write is not checked

(errcheck)


292-292: Error return value of h.QueryAllBizStatusData is not checked

(errcheck)

🔇 Additional comments (20)
module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go (1)

8-8: LGTM: Clean import change for zaplogger

The import change aligns with the PR objective of transitioning to zaplogger.

report_server/server.go (1)

7-7: LGTM on zaplogger import

The import aligns with the PR objective of transitioning to zaplogger.

go.mod (1)

Line range hint 1-100: Consider removing logrus as direct dependency

Since the PR's objective is to transition from logrus to zaplogger, consider removing github.com/sirupsen/logrus from direct dependencies if it's no longer needed. This will prevent potential confusion and ensure consistent logging practices.

suite/http/suite_test.go (3)

14-15: LGTM: Import changes align with logging transition.

The new logging imports are correctly structured and properly aliased.


86-86: LGTM: Context propagation improvement.

The addition of context parameter to NewHttpTunnel aligns with Go best practices for context propagation.


Line range hint 88-94: Verify logging pattern consistency across the codebase.

Let's ensure the logging pattern used here is consistent with other files that have been transitioned to the new logging system.

✅ Verification successful

Based on the search results, I can now generate the final response as the logging pattern verification is complete.

Logging pattern is consistent across the codebase

The error logging pattern log.G(ctx).WithError(err).Error() is consistently used across the codebase, including:

  • Similar tunnel start error handling in suite/mqtt/suite_test.go
  • Manager start error handling in multiple files
  • HTTP tunnel port parsing in cmd/module-controller/main.go
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar error logging patterns to ensure consistency
rg -A 2 'log\.G\(ctx\)\.WithError\(err\)\.Error\(' --type go

Length of output: 1401

module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go (1)

7-7: LGTM: Clean import addition

The zaplogger import is correctly added and aligns with the PR objective of transitioning to zap logger.

suite/mqtt/suite_test.go (2)

115-116: LGTM: Context handling changes look consistent

The changes follow a good pattern of:

  1. Passing context during initialization
  2. Removing context from method parameters where it's stored in the struct

Let's verify this pattern is consistent across other tunnel implementations:

✅ Verification successful

LGTM: Context handling pattern is consistently implemented

The verification confirms that both tunnel implementations (HTTP and MQTT) follow the same consistent pattern:

  • Both HttpTunnel and MqttTunnel structs store context.Context as a field
  • Both NewHttpTunnel and NewMqttTunnel accept context as their first parameter
  • Both implementations have removed context from their Start(clientID, env string) method signatures
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other tunnel implementations to verify context handling pattern
ast-grep --pattern 'type $tunnel struct {
  $$$
}'

# Search for NewXxxTunnel functions to verify context parameter
ast-grep --pattern 'func New$_Tunnel($$$) {
  $$$
}'

Length of output: 107


Script:

#!/bin/bash
# Let's try a more specific search for tunnel implementations
rg -l "Tunnel" --type go

# Then let's examine the content of these files
rg "type.*Tunnel.*struct|func New.*Tunnel|Start\(" --type go -A 3

Length of output: 8452


14-15: Verify logger initialization consistency

The code sets up both zap logger (logf.SetLogger(zap.New(...))) and logrus logger (log.L = logruslogger.FromLogrus(...)). This might lead to inconsistent logging behavior.

Let's check if this dual logger setup is intentional or if there are other files with similar patterns:

✅ Verification successful

Let me gather more context about the logging setup to understand if this is a test-specific pattern or a broader issue.


Dual logger setup is intentional for test environment

The dual logger initialization is a deliberate pattern used specifically in test files, where:

  • logf.SetLogger(zap) is used for controller-runtime logging in tests
  • log.L = logruslogger.FromLogrus() is used for virtual-kubelet logging in tests

This is consistent across test suites and matches the production setup in cmd/module-controller/main.go where both loggers are also initialized. No action is needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar dual logger initialization patterns
rg -U "SetLogger.*zap.*\n.*logruslogger.FromLogrus"

Length of output: 451


Script:

#!/bin/bash
# Check for logger initialization patterns in non-test files
rg -g '!*_test.go' "SetLogger|logruslogger.FromLogrus"

# Check imports of logging packages in test files
rg -g '*_test.go' "\"github.com/virtual-kubelet/virtual-kubelet/log\"|logf \"sigs.k8s.io/controller-runtime/pkg/log\""

Length of output: 718

cmd/module-controller/main.go (2)

89-90: LGTM! Error handling properly updated

The error handling has been correctly migrated to use zaplogger with appropriate error context and default value handling.

Also applies to: 95-96


203-210: 🛠️ Refactor suggestion

Consider graceful shutdown instead of panic

While the logging is properly updated, using panic for tunnel startup failures might not be the best approach. Consider implementing a graceful shutdown:

 if startFailedCount > 0 {
-    panic(errors.New(fmt.Sprintf("failed to start %d tunnels", startFailedCount)))
+    zlogger.Error(fmt.Errorf("failed to start %d tunnels", startFailedCount), "shutting down")
+    cancel() // trigger graceful shutdown
+    return nil
 } else if successTunnelCount == 0 {
-    panic(errors.New(fmt.Sprintf("successfully started 0 tunnels")))
+    zlogger.Error(fmt.Errorf("no tunnels started successfully"), "shutting down")
+    cancel() // trigger graceful shutdown
+    return nil
 }

Likely invalid or redundant comment.

common/utils/utils.go (1)

6-6: LGTM: Import statement is correctly placed

module_tunnels/koupleless_http_tunnel/http_tunnel.go (3)

29-29: LGTM: Good context management implementation

The addition of the context field and its proper initialization in the constructor follows best practices for context propagation.

Also applies to: 55-63


327-329: LGTM: Well-structured logging implementation

Good use of structured logging with proper field enrichment using WithValues. This provides better context for debugging and monitoring.

Also applies to: 359-361


Line range hint 1-377: Verify context propagation in dependent packages

The context implementation looks good, but we should verify that dependent packages properly handle the propagated context.

✅ Verification successful

Context propagation implementation is correctly handled

The verification shows that context is properly propagated throughout the codebase:

  1. The HttpTunnel struct correctly passes its context to all arkService method calls
  2. The ark_service.Service methods properly:
    • Accept context as their first parameter
    • Use context in HTTP requests via SetContext(ctx)
    • Utilize context for logging via zaplogger.FromContext(ctx)
  3. All HTTP client calls are context-aware through resty client's context support
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if other packages properly handle the propagated context
ast-grep --pattern 'func $_(ctx context.Context, $$$) {
  $$$
  h.arkService.$_($$$)
  $$$
}'

Length of output: 95


Script:

#!/bin/bash
# Let's check for context propagation in ark_service package
rg -A 5 "func.*\(ctx context.Context" module_tunnels/koupleless_http_tunnel/ark_service/

Length of output: 2589


Script:

#!/bin/bash
# Check how context is used in method calls to arkService
rg -A 3 "arkService\." module_tunnels/koupleless_http_tunnel/

Length of output: 1447


Script:

#!/bin/bash
# Check the Service struct and its methods in ark_service
fd -e go . module_tunnels/koupleless_http_tunnel/ark_service/ -x cat {}

Length of output: 4410

controller/module_deployment_controller/module_deployment_controller.go (3)

46-46: LGTM: Proper context-based logging implementation

The logging implementation correctly uses context and includes relevant request information.


Line range hint 62-164: LGTM: Comprehensive logging implementation in setup process

The logging implementation provides good visibility into the controller setup process with appropriate error handling and context usage.


350-357: LGTM: Well-implemented error logging

The error logging provides good context and properly uses the zaplogger with context.

module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (2)

11-11: Import zaplogger for structured logging

The zaplogger package is imported to replace the previous logging library. Ensure all logging statements are updated to use zaplogger for consistency and improved logging capabilities.


87-88: 🛠️ Refactor suggestion

Pass context explicitly in utility functions

When calling utility functions like utils.OnBaseUnreachable, pass the context explicitly rather than using a field from the struct. This maintains consistent context propagation throughout the application.

Modify the call:

-utils.OnBaseUnreachable(mqttTunnel.ctx, nodeName, mqttTunnel.kubeClient)
+utils.OnBaseUnreachable(ctx, nodeName, mqttTunnel.kubeClient)

Ensure ctx is passed to the OnNodeNotReady method.

Likely invalid or redundant comment.

Comment on lines +22 to +122
if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("installBiz request failed")
logger.Error(err, "installBiz request failed")
return
}

if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal InstallBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal InstallBiz response: %s", resp.Body()))
return
}

return response, nil
}

func (h *Service) UninstallBiz(ctx context.Context, req UninstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
logger := zaplogger.FromContext(ctx)

resp, err := h.client.R().
SetContext(ctx).
SetBody(req).
Post(fmt.Sprintf("http://%s:%d/uninstallBiz", baseIP, arkletPort))

if err != nil {
log.G(ctx).WithError(err).Errorf("uninstall request failed")
logger.Error(err, "uninstall request failed")
return
}

if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("uninstall request failed")
logger.Error(err, "uninstall request failed")
return
}

if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal UnInstallBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal UnInstallBiz response: %s", resp.Body()))
return
}

return response, nil
}

func (h *Service) QueryAllBiz(ctx context.Context, baseIP string, port int) (response QueryAllBizResponse, err error) {
logger := zaplogger.FromContext(ctx)

resp, err := h.client.R().
SetContext(ctx).
SetBody(`{}`).
Post(fmt.Sprintf("http://%s:%d/queryAllBiz", baseIP, port))

if err != nil {
log.G(ctx).WithError(err).Errorf("queryAllBiz request failed")
logger.Error(err, "queryAllBiz request failed")
return
}

if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("queryAllBiz request failed")
logger.Error(err, "queryAllBiz request failed")
return
}

if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal QueryAllBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal QueryAllBiz response: %s", resp.Body()))
return
}

return
}

func (h *Service) Health(ctx context.Context, baseIP string, arkletPort int) (response HealthResponse, err error) {
logger := zaplogger.FromContext(ctx)

resp, err := h.client.R().
SetContext(ctx).
SetBody(`{}`).
Post(fmt.Sprintf("http://%s:%d/health", baseIP, arkletPort))

if err != nil {
log.G(ctx).WithError(err).Errorf("health request failed")
logger.Error(err, "health request failed")
return
}

if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("health request failed")
logger.Error(err, "health request failed")
return
}

if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal Health response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal Health response: %s", resp.Body()))
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring common HTTP client operations

All methods follow the same pattern for HTTP requests and error handling. Consider extracting this into a helper method to reduce code duplication and improve maintainability.

+func (h *Service) doRequest(ctx context.Context, endpoint string, body interface{}, response interface{}) error {
+	logger := zaplogger.FromContext(ctx)
+	
+	resp, err := h.client.R().
+		SetContext(ctx).
+		SetBody(body).
+		Post(endpoint)
+	
+	if err != nil {
+		logger.Error(err, "request failed")
+		return err
+	}
+	
+	if resp == nil {
+		err = errors.New("received nil response from the server")
+		logger.Error(err, "request failed")
+		return err
+	}
+	
+	if err = json.Unmarshal(resp.Body(), response); err != nil {
+		logger.Error(err, fmt.Sprintf("unmarshal response failed: %s", resp.Body()))
+		return err
+	}
+	
+	return nil
+}

 func (h *Service) InstallBiz(ctx context.Context, req InstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
-	logger := zaplogger.FromContext(ctx)
-	
-	resp, err := h.client.R().
-		SetContext(ctx).
-		SetBody(req).
-		Post(fmt.Sprintf("http://%s:%d/installBiz", baseIP, arkletPort))
-	
-	if err != nil {
-		logger.Error(err, "installBiz request failed")
-		return
-	}
-	// ... rest of error handling
+	err = h.doRequest(
+		ctx,
+		fmt.Sprintf("http://%s:%d/installBiz", baseIP, arkletPort),
+		req,
+		&response,
+	)
 	return response, err
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger := zaplogger.FromContext(ctx)
resp, err := h.client.R().
SetContext(ctx).
SetBody(req).
Post(fmt.Sprintf("http://%s:%d/installBiz", baseIP, arkletPort))
if err != nil {
log.G(ctx).WithError(err).Errorf("installBiz request failed")
logger.Error(err, "installBiz request failed")
return
}
if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("installBiz request failed")
logger.Error(err, "installBiz request failed")
return
}
if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal InstallBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal InstallBiz response: %s", resp.Body()))
return
}
return response, nil
}
func (h *Service) UninstallBiz(ctx context.Context, req UninstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
logger := zaplogger.FromContext(ctx)
resp, err := h.client.R().
SetContext(ctx).
SetBody(req).
Post(fmt.Sprintf("http://%s:%d/uninstallBiz", baseIP, arkletPort))
if err != nil {
log.G(ctx).WithError(err).Errorf("uninstall request failed")
logger.Error(err, "uninstall request failed")
return
}
if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("uninstall request failed")
logger.Error(err, "uninstall request failed")
return
}
if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal UnInstallBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal UnInstallBiz response: %s", resp.Body()))
return
}
return response, nil
}
func (h *Service) QueryAllBiz(ctx context.Context, baseIP string, port int) (response QueryAllBizResponse, err error) {
logger := zaplogger.FromContext(ctx)
resp, err := h.client.R().
SetContext(ctx).
SetBody(`{}`).
Post(fmt.Sprintf("http://%s:%d/queryAllBiz", baseIP, port))
if err != nil {
log.G(ctx).WithError(err).Errorf("queryAllBiz request failed")
logger.Error(err, "queryAllBiz request failed")
return
}
if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("queryAllBiz request failed")
logger.Error(err, "queryAllBiz request failed")
return
}
if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal QueryAllBiz response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal QueryAllBiz response: %s", resp.Body()))
return
}
return
}
func (h *Service) Health(ctx context.Context, baseIP string, arkletPort int) (response HealthResponse, err error) {
logger := zaplogger.FromContext(ctx)
resp, err := h.client.R().
SetContext(ctx).
SetBody(`{}`).
Post(fmt.Sprintf("http://%s:%d/health", baseIP, arkletPort))
if err != nil {
log.G(ctx).WithError(err).Errorf("health request failed")
logger.Error(err, "health request failed")
return
}
if resp == nil {
err = errors.New("received nil response from the server")
log.G(ctx).WithError(err).Errorf("health request failed")
logger.Error(err, "health request failed")
return
}
if err = json.Unmarshal(resp.Body(), &response); err != nil {
log.G(ctx).WithError(err).Errorf("Unmarshal Health response: %s", resp.Body())
logger.Error(err, fmt.Sprintf("Unmarshal Health response: %s", resp.Body()))
func (h *Service) doRequest(ctx context.Context, endpoint string, body interface{}, response interface{}) error {
logger := zaplogger.FromContext(ctx)
resp, err := h.client.R().
SetContext(ctx).
SetBody(body).
Post(endpoint)
if err != nil {
logger.Error(err, "request failed")
return err
}
if resp == nil {
err = errors.New("received nil response from the server")
logger.Error(err, "request failed")
return err
}
if err = json.Unmarshal(resp.Body(), response); err != nil {
logger.Error(err, fmt.Sprintf("unmarshal response failed: %s", resp.Body()))
return err
}
return nil
}
func (h *Service) InstallBiz(ctx context.Context, req InstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
err = h.doRequest(
ctx,
fmt.Sprintf("http://%s:%d/installBiz", baseIP, arkletPort),
req,
&response,
)
return response, err
}
func (h *Service) UninstallBiz(ctx context.Context, req UninstallBizRequest, baseIP string, arkletPort int) (response ArkResponse, err error) {
err = h.doRequest(
ctx,
fmt.Sprintf("http://%s:%d/uninstallBiz", baseIP, arkletPort),
req,
&response,
)
return response, err
}
func (h *Service) QueryAllBiz(ctx context.Context, baseIP string, port int) (response QueryAllBizResponse, err error) {
err = h.doRequest(
ctx,
fmt.Sprintf("http://%s:%d/queryAllBiz", baseIP, port),
struct{}{},
&response,
)
return response, err
}
func (h *Service) Health(ctx context.Context, baseIP string, arkletPort int) (response HealthResponse, err error) {
err = h.doRequest(
ctx,
fmt.Sprintf("http://%s:%d/health", baseIP, arkletPort),
struct{}{},
&response,
)
return response, err
}

@@ -5,8 +5,7 @@
"encoding/json"
"errors"
"fmt"

"github.com/koupleless/virtual-kubelet/common/log"
"github.com/koupleless/module_controller/common/zaplogger"
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Old logging dependencies found in test files

The verification reveals that while the main application code has been migrated to zaplogger, there are still old logging dependencies in test files:

  • Test files (suite/mqtt/suite_test.go, suite/http/suite_test.go) still import and use github.com/sirupsen/logrus
  • Test files use old logging patterns like log.G(ctx).WithError()

These should be updated to use the new zaplogger implementation for consistency.

🔗 Analysis chain

Verify complete removal of old logging implementation

Let's ensure all old logging imports and usages have been removed.


Let's verify the actual usage context of these logging references to determine if they are legitimate dependencies or remnants of old logging.


Let's check one final thing to verify if the old logging is actually being imported and used in the test files:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of old logging implementations
echo "Checking for old logging imports..."
rg -l "github.com/koupleless/virtual-kubelet/common/log|logrus|logr" 

echo "Checking for old logging patterns..."
rg -l "WithError|G\(ctx\)"

Length of output: 614


Script:

#!/bin/bash
# Check the actual usage context in the found files
echo "Checking logging imports in go.mod..."
rg "logrus|logr" go.mod -A 2 -B 2

echo "Checking logging usage in test files..."
rg "WithError|G\(ctx\)" suite/mqtt/suite_test.go suite/http/suite_test.go -A 2 -B 2

echo "Checking zaplogger implementation..."
cat common/zaplogger/logger.go

echo "Checking main.go logging usage..."
rg "WithError|G\(ctx\)" cmd/module-controller/main.go -A 2 -B 2

Length of output: 4432


Script:

#!/bin/bash
# Check the imports in test files to see which logging package they're using
echo "Checking imports in test files..."
rg "^import \(" -A 10 suite/mqtt/suite_test.go suite/http/suite_test.go

echo "Checking imports in main.go..."
rg "^import \(" -A 10 cmd/module-controller/main.go

Length of output: 1994

@@ -109,6 +109,6 @@ func InitReportServer() {
http.HandleFunc("/log", logHandler)
log.Println("Starting server on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
logrus.Errorf("Server failed: %v", err)
zaplogger.GetLogger().Error(err, "Server failed: %v")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error logging format

The error is being logged twice in the format string. The zaplogger's Error method already handles the error object, so including it in the format string is redundant.

-    zaplogger.GetLogger().Error(err, "Server failed: %v")
+    zaplogger.GetLogger().Error(err, "Server failed")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
zaplogger.GetLogger().Error(err, "Server failed: %v")
zaplogger.GetLogger().Error(err, "Server failed")

go.mod Outdated
@@ -10,11 +10,12 @@ require (
github.com/go-resty/resty/v2 v2.11.0
github.com/google/uuid v1.6.0
github.com/koupleless/arkctl v0.2.2
github.com/koupleless/virtual-kubelet v0.3.5-0.20241125132309-00fba45cbde1
github.com/koupleless/virtual-kubelet v0.3.5-0.20241126075206-2ab835c98a84
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Virtual-kubelet version updates need attention due to logging conflicts

The verification reveals several concerns:

  • Both logrus and zap logging libraries are present in the codebase
  • Logrus is still actively used in multiple files (cmd/module-controller/main.go, suite/mqtt/suite_test.go, etc.)
  • The updated virtual-kubelet dependencies don't introduce any breaking changes in logging, but the current implementation maintains both logging systems

Action items:

  • The logrus dependency and its imports should be removed as part of the transition to zaplogger
  • Files still using logrus need to be updated to use the new zaplogger implementation
  • Consider removing the direct logrus dependency from go.mod after all usages are migrated
🔗 Analysis chain

Verify the virtual-kubelet version updates

The changes include updating koupleless/virtual-kubelet and adding virtual-kubelet/virtual-kubelet. Please ensure these specific versions are compatible with the new zaplogger implementation.

Also applies to: 18-18


Let me gather more information about the logging-related dependencies to verify the compatibility.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for logging-related changes in the virtual-kubelet dependencies

# Check the changelog or commit diff for the koupleless/virtual-kubelet update
gh api repos/koupleless/virtual-kubelet/compare/00fba45cbde1...2ab835c98a84 --jq '.commits[].commit.message'

# Check the v1.11.0 release notes of virtual-kubelet/virtual-kubelet
gh api repos/virtual-kubelet/virtual-kubelet/releases/tags/v1.11.0 --jq '.body'

Length of output: 4794


Script:

#!/bin/bash
# Check for logging-related imports and dependencies

# Check for logrus imports in the codebase
echo "=== Checking logrus imports ==="
rg "github.com/sirupsen/logrus" -l

# Check for zap imports in the codebase
echo -e "\n=== Checking zap imports ==="
rg "go.uber.org/zap" -l

# Check go.mod for logging-related dependencies
echo -e "\n=== Checking go.mod for logging dependencies ==="
cat go.mod | grep -E "logrus|zap|zapr"

Length of output: 611

@@ -152,23 +152,23 @@ func (c *Client) Connect() error {

// Pub publishes a message to a specified topic
func (c *Client) Pub(topic string, qos byte, msg []byte) error {
logrus.Infof("Publishing message: %s to topic: %s\n", msg, topic)
zaplogger.GetLogger().Info(fmt.Sprintf("Publishing message: %s to topic: %s\n", msg, topic))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use safe byte slice handling in log messages

The current logging of the message payload using %s format specifier might not be safe for all byte slices, especially if they contain non-UTF8 data.

-	zaplogger.GetLogger().Info(fmt.Sprintf("Publishing message: %s to topic: %s\n", msg, topic))
+	zaplogger.GetLogger().Info("Publishing message",
+		"payload_size", len(msg),
+		"topic", topic)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
zaplogger.GetLogger().Info(fmt.Sprintf("Publishing message: %s to topic: %s\n", msg, topic))
zaplogger.GetLogger().Info("Publishing message",
"payload_size", len(msg),
"topic", topic)

@@ -24,6 +23,7 @@ import (
var _ tunnel.Tunnel = &MqttTunnel{}

type MqttTunnel struct {
ctx context.Context
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid storing context.Context within struct fields

Storing context.Context inside a struct (MqttTunnel.ctx) is not recommended as it can lead to unintended consequences, especially in concurrent code. Contexts are meant to be passed explicitly to functions that need them. This ensures that cancellation and deadlines are propagated correctly.

Apply this diff to remove the ctx field and pass context explicitly:

 type MqttTunnel struct {
-	ctx context.Context
 	sync.Mutex
 	// other fields...
 }

-func NewMqttTunnel(ctx context.Context, env string, kubeClient client.Client, moduleDeploymentController *module_deployment_controller.ModuleDeploymentController) MqttTunnel {
-	return MqttTunnel{
-		ctx:                        ctx,
+func NewMqttTunnel(env string, kubeClient client.Client, moduleDeploymentController *module_deployment_controller.ModuleDeploymentController) MqttTunnel {
+	return MqttTunnel{
 		env:                        env,
 		kubeClient:                 kubeClient,
 		moduleDeploymentController: moduleDeploymentController,
 	}
 }

Also applies to: 43-45

@@ -117,20 +119,20 @@
ClientKeyPath: c.MqttClientKeyPath,
CleanSession: true,
OnConnectHandler: func(client paho.Client) {
log.G(ctx).Info("MQTT client connected :", clientID)
zlogger.Info(fmt.Sprintf("MQTT client connected: %s", clientID))
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Leverage structured logging instead of fmt.Sprintf

The log messages use fmt.Sprintf for string formatting, which is unnecessary with structured logging. Replace these with structured fields to improve log parsing and querying.

For example, change:

zlogger.Info(fmt.Sprintf("MQTT client connected: %s", clientID))

To:

zlogger.Info("MQTT client connected", zap.String("clientID", clientID))

Apply similar changes to other logging statements at lines 146, 166, 197, 222, 247, and 270.

Also applies to: 146-146, 166-166, 197-197, 222-222, 247-247, 270-270

client.Subscribe(fmt.Sprintf(model.BaseHeartBeatTopic, mqttTunnel.env), mqtt.Qos1, mqttTunnel.heartBeatMsgCallback)
client.Subscribe(fmt.Sprintf(model.BaseQueryBaselineTopic, mqttTunnel.env), mqtt.Qos1, mqttTunnel.queryBaselineMsgCallback)
},
})

err = mqttTunnel.mqttClient.Connect()
if err != nil {
log.G(ctx).WithError(err).Error("mqtt connect error")
zlogger.Error(err, "mqtt connect error")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct usage of zaplogger.Error method

The Error method should include the error as a field rather than a parameter. Adjust the logging statements to correctly utilize zap.Error.

Replace instances like:

zlogger.Error(err, "Error unmarshalling heart beat data")

With:

zlogger.Error("Error unmarshalling heart beat data", zap.Error(err))

Apply this correction to all error logging statements at lines 130, 151, 172, 188, 203, 228, 253, and 276.

Also applies to: 151-151, 172-172, 188-188, 203-203, 228-228, 253-253, 276-276

Comment on lines +314 to +315
zlogger := zaplogger.FromContext(mqttTunnel.ctx).WithValues("bizName", bizModel.BizName, "bizVersion", bizModel.BizVersion)
zlogger.Info("InstallModule")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use With and structured fields for logger context

The method WithValues is not part of the zap logger API. Use With in combination with structured fields to add context to logs.

Replace:

zlogger := zaplogger.FromContext(mqttTunnel.ctx).WithValues("bizName", bizModel.BizName, "bizVersion", bizModel.BizVersion)

With:

zlogger := zaplogger.FromContext(mqttTunnel.ctx).With(
    zap.String("bizName", bizModel.BizName),
    zap.String("bizVersion", bizModel.BizVersion),
)

Apply similar changes to lines 324-325.

Also applies to: 324-325

Comment on lines +135 to 138
<-mqttTunnel.ctx.Done()
mqttTunnel.mqttClient.Disconnect()
}()
mqttTunnel.ready = true
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure proper context handling in goroutines

When using context.Context within goroutines, passing the context as a parameter is safer than accessing a struct field. This prevents potential data races and ensures that context cancellations are handled correctly.

Modify the goroutine to accept ctx explicitly:

 go func(ctx context.Context) {
 	<-ctx.Done()
 	mqttTunnel.mqttClient.Disconnect()
-}() 
+}(ctx)

Committable suggestion skipped: line range outside the PR's diff.

Copy link

codecov bot commented Nov 26, 2024

Codecov Report

Attention: Patch coverage is 62.36559% with 70 lines in your changes missing coverage. Please review.

Project coverage is 78.33%. Comparing base (1c07ba4) to head (2fd5620).
Report is 13 commits behind head on main.

Files with missing lines Patch % Lines
...dule_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go 65.06% 27 Missing and 2 partials ⚠️
...loyment_controller/module_deployment_controller.go 44.00% 14 Missing ⚠️
.../koupleless_http_tunnel/ark_service/ark_service.go 25.00% 12 Missing ⚠️
...dule_tunnels/koupleless_http_tunnel/http_tunnel.go 72.41% 8 Missing ⚠️
common/zaplogger/logger.go 70.00% 3 Missing ⚠️
common/utils/utils.go 85.71% 2 Missing ⚠️
module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go 83.33% 1 Missing ⚠️
report_server/server.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #22      +/-   ##
==========================================
- Coverage   81.29%   78.33%   -2.96%     
==========================================
  Files          12       13       +1     
  Lines        1834     1939     +105     
==========================================
+ Hits         1491     1519      +28     
- Misses        287      351      +64     
- Partials       56       69      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@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: 3

🧹 Outside diff range and nitpick comments (4)
cmd/module-controller/main.go (4)

21-21: Clean up logging-related imports

The code imports both the new zaplogger and the old logging libraries (logrus, logruslogger). Since we're migrating to zaplogger, consider removing the old logging imports once the migration is complete.

-"github.com/sirupsen/logrus"
-"github.com/virtual-kubelet/virtual-kubelet/log"
-logruslogger "github.com/virtual-kubelet/virtual-kubelet/log/logrus"

Also applies to: 43-44


Line range hint 159-169: Complete logger migration for health checks

The health check error logging still uses the old logging pattern (log.G(ctx)). Update these to use the new zaplogger for consistency.

 if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
-    log.G(ctx).Error(err, "unable to set up health check")
+    zlogger.Error(err, "unable to set up health check")
     os.Exit(1)
 }
 if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
-    log.G(ctx).Error(err, "unable to set up ready check")
+    zlogger.Error(err, "unable to set up ready check")
     os.Exit(1)
 }

Line range hint 190-193: Update HTTP tunnel error logging

The HTTP tunnel port parsing error still uses the old logging pattern (log.G(ctx)). Update this to use the new zaplogger for consistency.

 if err != nil {
-    log.G(ctx).WithError(err).Error("failed to parse HTTP_TUNNEL_LISTEN_PORT, set default port 7777")
+    zlogger.Error(err, "failed to parse HTTP_TUNNEL_LISTEN_PORT, set default port 7777")
     httpTunnelListenPort = 7777
 }

Line range hint 214-219: Consider more graceful error handling for tunnel startup

Instead of using panic with fmt.Sprintf, consider:

  1. Using structured error types
  2. Logging the error with zaplogger before exiting
  3. Using os.Exit(1) for a more graceful shutdown
 if startFailedCount > 0 {
-    panic(errors.New(fmt.Sprintf("failed to start %d tunnels", startFailedCount)))
+    zlogger.Error(fmt.Errorf("failed to start %d tunnels", startFailedCount), "tunnel startup failed")
+    os.Exit(1)
 } else if successTunnelCount == 0 {
-    panic(errors.New(fmt.Sprintf("successfully started 0 tunnels")))
+    zlogger.Error(fmt.Errorf("successfully started 0 tunnels"), "no tunnels started")
+    os.Exit(1)
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f5f1325 and ce1acc2.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • cmd/module-controller/main.go (8 hunks)
  • cmd/self-test/main.go (1 hunks)
  • common/utils/utils.go (4 hunks)
  • common/zaplogger/logger.go (1 hunks)
  • controller/module_deployment_controller/module_deployment_controller.go (14 hunks)
  • go.mod (1 hunks)
  • module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go (2 hunks)
  • module_tunnels/koupleless_http_tunnel/http_tunnel.go (22 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go (3 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (15 hunks)
  • report_server/server.go (2 hunks)
  • suite/http/mock_http_base.go (1 hunks)
  • suite/http/suite_test.go (2 hunks)
  • suite/mqtt/mock_mqtt_base.go (1 hunks)
  • suite/mqtt/module_mqtt_lifecycle_test.go (1 hunks)
  • suite/mqtt/suite_test.go (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • common/zaplogger/logger.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • cmd/self-test/main.go
  • common/utils/utils.go
  • controller/module_deployment_controller/module_deployment_controller.go
  • go.mod
  • module_tunnels/koupleless_http_tunnel/ark_service/ark_service.go
  • module_tunnels/koupleless_mqtt_tunnel/mqtt/mqtt.go
  • report_server/server.go
  • suite/http/mock_http_base.go
  • suite/http/suite_test.go
  • suite/mqtt/mock_mqtt_base.go
  • suite/mqtt/module_mqtt_lifecycle_test.go
  • suite/mqtt/suite_test.go

@@ -56,7 +57,7 @@
return utils.GetBizIdentity(container.Name, utils.GetBizVersionFromContainer(container))
}

func (mqttTunnel *MqttTunnel) RegisterNode(ctx context.Context, nodeInfo vkModel.NodeInfo) {
func (mqttTunnel *MqttTunnel) RegisterNode(nodeInfo vkModel.NodeInfo) {
nodeID := utils2.ExtractNodeIDFromNodeName(nodeInfo.Metadata.Name)
mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle error return value from mqttClient.Sub

The error return value from mqttClient.Sub is not being checked. This could lead to silent failures in subscription.

Apply this diff to handle the error:

-mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback)
+if err := mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback); err != nil {
+    zlogger.Error("Failed to subscribe to health topic", zap.Error(err))
+    return
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback)
if err := mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback); err != nil {
zlogger.Error("Failed to subscribe to health topic", zap.Error(err))
return
}
🧰 Tools
🪛 golangci-lint (1.62.2)

62-62: Error return value of mqttTunnel.mqttClient.Sub is not checked

(errcheck)

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var _ tunnel.Tunnel = &HttpTunnel{}

type HttpTunnel struct {
ctx context.Context
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid storing context.Context in struct fields

It is recommended in Go to pass context.Context explicitly to each function that needs it, rather than storing it within a struct. Storing ctx in the HttpTunnel struct can lead to unintended behaviors, especially with context cancellations and timeouts.

Consider removing the ctx field from the HttpTunnel struct and passing ctx explicitly to methods that require it.

if data.Response.Code == "SUCCESS" {
if data.Command == model.CommandInstallBiz {
// not update here, update in all biz response callback
return
}
} else {
// operation failed, log
logrus.Errorf("biz operation failed: %s\n%s\n%s", data.Response.Message, data.Response.ErrorStackTrace, data.Response.Data.Message)
logger.Error(nil, "biz operation failed: %s\n%s\n%s", data.Response.Message, data.Response.ErrorStackTrace, data.Response.Data.Message)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use structured logging with logger.Error instead of formatted strings

The logger.Error method is designed for structured logging and does not support formatted strings in the message parameter. Passing a formatted string may not produce the intended log output.

Update the log statement to use key-value pairs for additional context:

-logger.Error(nil, "biz operation failed: %s\n%s\n%s", data.Response.Message, data.Response.ErrorStackTrace, data.Response.Data.Message)
+logger.Error(nil, "biz operation failed",
+    "message", data.Response.Message,
+    "errorStackTrace", data.Response.ErrorStackTrace,
+    "dataMessage", data.Response.Data.Message)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.Error(nil, "biz operation failed: %s\n%s\n%s", data.Response.Message, data.Response.ErrorStackTrace, data.Response.Data.Message)
logger.Error(nil, "biz operation failed",
"message", data.Response.Message,
"errorStackTrace", data.Response.ErrorStackTrace,
"dataMessage", data.Response.Data.Message)

Copy link

@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

🧹 Outside diff range and nitpick comments (3)
common/utils/utils.go (1)

177-189: Consider adding input validation for robustness

The function logic is correct and more straightforward. However, consider adding input validation for the records slice.

 func GetLatestState(records []ark.ArkBizStateRecord) (time.Time, string, string) {
+    if len(records) == 0 {
+        return time.Time{}, "", ""
+    }
     latestStateTime := int64(0)
     reason := ""
     message := ""
common/utils/utils_test.go (2)

Line range hint 354-369: Remove or uncomment the PodKey field.

The commented out PodKey field suggests incomplete changes. Either remove it if it's no longer needed or uncomment it if it's still required. This will improve code clarity.


Line range hint 633-635: Enhance TestOnBaseUnreachable coverage.

The test for OnBaseUnreachable is minimal and doesn't verify the actual behavior. Consider adding assertions to verify:

  • Expected client calls
  • Error handling
  • State changes
 func TestOnBaseUnreachable(t *testing.T) {
-    OnBaseUnreachable(context.Background(), "test-node-name", fake.NewFakeClient())
+    // Setup
+    client := fake.NewFakeClient()
+    ctx := context.Background()
+    nodeName := "test-node-name"
+    
+    // Execute
+    OnBaseUnreachable(ctx, nodeName, client)
+    
+    // Verify expected behavior
+    // Add assertions here to verify the function's effects
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ce1acc2 and 5a41d73.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • common/model/model.go (2 hunks)
  • common/utils/utils.go (4 hunks)
  • common/utils/utils_test.go (10 hunks)
  • go.mod (1 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (14 hunks)
  • suite/http/mock_http_base.go (2 hunks)
  • suite/mqtt/mock_mqtt_base.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • go.mod
  • suite/http/mock_http_base.go
  • suite/mqtt/mock_mqtt_base.go
🔇 Additional comments (14)
common/model/model.go (2)

4-4: LGTM: Import addition is appropriate

The new import for the ark package is correctly specified and necessary for the new LatestStateRecord field type.


57-60: Verify compatibility with existing code

The struct fields are well-formatted with consistent alignment and clear documentation. However, since we're adding a new field to an existing struct, we should verify:

  1. JSON serialization compatibility with existing clients
  2. All code locations that create or modify this struct are updated

Let's verify the struct usage:

✅ Verification successful

Let's search for more specific patterns to understand the usage and potential impacts of the changes.


Field addition is safe and compatible

Based on the verification results, the addition of the LatestStateRecord field is safe because:

  1. The struct is used in a limited scope, primarily in test code and mock implementations
  2. The only production usage is in TranslateSimpleBizDataToArkBizInfo which has been properly updated to handle the new field
  3. No direct JSON marshaling/unmarshaling operations were found, reducing compatibility concerns
  4. The field is added with proper JSON tags following the existing convention

The changes maintain backward compatibility as JSON deserializers will ignore unknown fields, and the new field is properly handled in all identified usages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for struct instantiations and field access
rg -t go "ArkSimpleBizInfoData" -A 5

# Search for JSON marshaling/unmarshaling of this struct
ast-grep --pattern 'json.Marshal($_)' | rg "ArkSimpleBizInfoData"
ast-grep --pattern 'json.Unmarshal($_)' | rg "ArkSimpleBizInfoData"

Length of output: 2630


Script:

#!/bin/bash
# Search for any code that creates or modifies ArkSimpleBizInfoData
ast-grep --pattern 'ArkSimpleBizInfoData{$$$}'

# Search for any code that uses the struct in JSON operations
rg -t go "json\.(Marshal|Unmarshal).*(ArkSimpleBizInfoData|ArkSimpleAllBizInfoData)"

# Search for any code that accesses the struct fields
rg -t go "\.(Name|Version|State|LatestStateRecord)\s*="

Length of output: 248

common/utils/utils.go (5)

6-6: LGTM: Import change aligns with PR objective

The addition of the zaplogger import is consistent with the PR's goal of transitioning to zap logger.


133-133: LGTM: Improved state retrieval logic

The change simplifies state retrieval by using the GetLatestState function while maintaining the core functionality.


169-172: LGTM: Correct field mapping implementation

The function correctly maps the simple data fields to the ArkBizInfo struct while maintaining proper field alignment.


197-208: LGTM: Well-structured logging implementation

The logging implementation follows best practices with proper context and field structuring.


218-218: ⚠️ Potential issue

Improve error logging and default value handling

The current implementation passes nil as error and uses string formatting instead of structured logging.

-zaplogger.GetLogger().Error(nil, fmt.Sprintf("failed to parse port %s from node info", portStr))
+zaplogger.GetLogger().Error(err, "Failed to parse port from node info",
+    "port", portStr,
+    "defaultPort", 1238)

-port = 1238
+const defaultTunnelPort = 1238  // Add at top of file
+port = defaultTunnelPort
module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (4)

27-27: Avoid storing context.Context in struct fields

Storing context.Context inside a struct is not recommended as it can lead to unintended consequences in concurrent code.


123-123: Use structured logging instead of string formatting

Replace fmt.Sprintf with structured logging fields for better log parsing and querying.

Also applies to: 147-147, 167-167, 198-198, 223-223, 248-248, 271-271


131-131: Use zap.Error for error logging

The error should be included as a field rather than a parameter in the logging statements.

Also applies to: 152-152, 173-173, 189-189, 204-204, 229-229, 254-254, 277-277


340-341: Use zap.With for structured logging fields

Replace WithValues with With and appropriate zap field constructors.

Also applies to: 350-351

common/utils/utils_test.go (3)

418-440: LGTM! Well-structured test with clear assertions.

The test case properly validates the timestamp conversion and follows good testing practices with explicit expected values.


Line range hint 458-502: LGTM! Good coverage of edge cases.

The test cases properly handle edge scenarios for timestamps:

  • Zero timestamps
  • Negative timestamps
  • No matching records

Line range hint 600-623: LGTM! Improved test readability with testify/assert.

The switch to using testify/assert library makes the test assertions more readable and provides better error messages.

Comment on lines +282 to +324
if data.Data.Command == model.CommandInstallBiz {
if data.Data.Response.Code == "SUCCESS" {
mqttTunnel.onOneBizDataArrived(nodeName, vkModel.BizStatusData{
Key: utils.GetBizIdentity(data.Data.BizName, data.Data.BizVersion),
Name: data.Data.BizName,
State: string(vkModel.BizStateActivated),
ChangeTime: time.UnixMilli(data.PublishTimestamp),
Reason: fmt.Sprintf("%s:%s %s succeed", data.Data.BizName, data.Data.BizVersion, data.Data.Command),
Message: data.Data.Response.Data.Message,
})
} else {
mqttTunnel.onOneBizDataArrived(nodeName, vkModel.BizStatusData{
Key: utils.GetBizIdentity(data.Data.BizName, data.Data.BizVersion),
Name: data.Data.BizName,
State: string(vkModel.BizStateBroken),
ChangeTime: time.UnixMilli(data.PublishTimestamp),
Reason: fmt.Sprintf("%s:%s %s failed", data.Data.BizName, data.Data.BizVersion, data.Data.Command),
Message: data.Data.Response.Data.Message,
})
}
} else if data.Data.Command == model.CommandUnInstallBiz {
if data.Data.Response.Code == "SUCCESS" {
mqttTunnel.onOneBizDataArrived(nodeName, vkModel.BizStatusData{
Key: utils.GetBizIdentity(data.Data.BizName, data.Data.BizVersion),
Name: data.Data.BizName,
State: string(vkModel.BizStateStopped),
ChangeTime: time.UnixMilli(data.PublishTimestamp),
Reason: fmt.Sprintf("%s:%s %s succeed", data.Data.BizName, data.Data.BizVersion, data.Data.Command),
Message: data.Data.Response.Data.Message,
})
} else {
mqttTunnel.onOneBizDataArrived(nodeName, vkModel.BizStatusData{
Key: utils.GetBizIdentity(data.Data.BizName, data.Data.BizVersion),
Name: data.Data.BizName,
State: string(vkModel.BizStateBroken),
ChangeTime: time.UnixMilli(data.PublishTimestamp),
Reason: fmt.Sprintf("%s:%s %s failed", data.Data.BizName, data.Data.BizVersion, data.Data.Command),
Message: data.Data.Response.Data.Message,
})
}
} else {
unsupported := fmt.Sprintf("unsupport command: %s", data.Data.Command)
zlogger.Error(errors.New(unsupported), unsupported)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor command handling to reduce duplication

The command handling logic contains significant duplication in status creation. Consider:

  1. Creating a helper function to construct BizStatusData
  2. Using constants for state values instead of string literals
  3. Implementing a command pattern to handle different operations

Example refactor:

+func createBizStatusData(data *model.BizOperationResponse, timestamp int64, state vkModel.BizState) vkModel.BizStatusData {
+    return vkModel.BizStatusData{
+        Key:        utils.GetBizIdentity(data.BizName, data.BizVersion),
+        Name:       data.BizName,
+        State:      string(state),
+        ChangeTime: time.UnixMilli(timestamp),
+        Reason:     fmt.Sprintf("%s:%s %s %s", data.BizName, data.BizVersion, data.Command, 
+            map[bool]string{true: "succeed", false: "failed"}[data.Response.Code == "SUCCESS"]),
+        Message:    data.Response.Data.Message,
+    }
+}

-if data.Data.Command == model.CommandInstallBiz {
-    if data.Data.Response.Code == "SUCCESS" {
-        mqttTunnel.onOneBizDataArrived(nodeName, vkModel.BizStatusData{
-            Key:        utils.GetBizIdentity(data.Data.BizName, data.Data.BizVersion),
-            // ... repeated code
-        })
-    } else {
-        // ... similar repeated code
-    }
-} else if data.Data.Command == model.CommandUnInstallBiz {
-    // ... similar repeated code
-}
+state := map[string]map[bool]vkModel.BizState{
+    model.CommandInstallBiz:   {true: vkModel.BizStateActivated, false: vkModel.BizStateBroken},
+    model.CommandUnInstallBiz: {true: vkModel.BizStateStopped, false: vkModel.BizStateBroken},
+}[data.Data.Command]
+
+if state == nil {
+    zlogger.Error("Unsupported command", zap.String("command", data.Data.Command))
+    return
+}
+
+mqttTunnel.onOneBizDataArrived(nodeName, 
+    createBizStatusData(&data.Data, data.PublishTimestamp, 
+        state[data.Data.Response.Code == "SUCCESS"]))

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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: 3

🧹 Outside diff range and nitpick comments (8)
module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (2)

Line range hint 61-71: Add error handling for MQTT subscriptions

The subscription calls to mqttClient.Sub don't check for errors, which could lead to silent failures.

Add error handling for each subscription call. For example:

-mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback)
+if err := mqttTunnel.mqttClient.Sub(fmt.Sprintf(model.BaseHealthTopic, mqttTunnel.env, nodeID), mqtt.Qos1, mqttTunnel.healthMsgCallback); err != nil {
+    zlogger.Error("Failed to subscribe to health topic", zap.Error(err))
+    return
+}
🧰 Tools
🪛 golangci-lint (1.62.2)

63-63: Error return value of mqttTunnel.mqttClient.Sub is not checked

(errcheck)


Line range hint 180-191: Add error handling for JSON marshaling

The error from json.Marshal is ignored, which could lead to silent failures.

Add error handling for the marshal operation:

-        baselineBytes, _ := json.Marshal(baselineBizs)
+        baselineBytes, err := json.Marshal(baselineBizs)
+        if err != nil {
+            zlogger.Error("Failed to marshal baseline bizs", zap.Error(err))
+            return
+        }
common/utils/utils_test.go (6)

Line range hint 354-398: Consider using more realistic timestamp values in test data.

The hardcoded timestamp 1234 is not representative of real-world Unix millisecond timestamps. Consider using timestamps that reflect actual dates (e.g., 1699564800000 for "2023-11-10") to make the tests more realistic and maintainable.

-							ChangeTime: 1234,
+							ChangeTime: 1699564800000, // 2023-11-10

Line range hint 418-450: Enhance test readability with named constants and comments.

Consider using named constants for timestamps and adding comments to clarify the test scenario. This would make the test more maintainable and self-documenting.

+	// Define timestamps for better readability
+	const (
+		oldestTime  = 1234    // Oldest record
+		middleTime  = 12345   // Middle record
+		newestTime  = 123456  // Most recent record
+	)
+
 	records := []ark.ArkBizStateRecord{
 		{
 			State:      "state1",
-			ChangeTime: 1234,
+			ChangeTime: oldestTime,  // First state change
 			Reason:     "Reason1",
 			Message:    "Message1",
 		},
 		{
 			State:      "state2",
-			ChangeTime: 12345,
+			ChangeTime: middleTime,  // Second state change
 			Reason:     "Reason2",
 			Message:    "Message2",
 		},
 		{
 			State:      "state1",
-			ChangeTime: 123456,
+			ChangeTime: newestTime,  // Most recent state change
 			Reason:     "Reason3",
 			Message:    "Message3",
 		},
 	}

Line range hint 458-477: Replace negative timestamp with a valid Unix millisecond value.

Using a negative timestamp (-1234) is not realistic for Unix timestamps. Consider using a valid timestamp value, even for error cases.

-			ChangeTime: -1234,
+			ChangeTime: 0,  // Use 0 or another valid timestamp for error cases

Line range hint 486-513: Add test documentation to clarify the purpose.

The test name and implementation could be more explicit about what constitutes an "invalid" change time and what behavior is being verified.

+// TestGetLatestStateInvalidChangeTime verifies that when encountering a record
+// with a zero timestamp (invalid), the function correctly selects the record
+// with the next valid timestamp.
 func TestGetLatestStateInvalidChangeTime(t *testing.T) {

600-601: Improve assertion error message.

The error message in the assertion doesn't clearly indicate what's being compared. Consider making it more specific to the length comparison.

-		assert.Equal(t, len(result), len(tc.expected), fmt.Errorf("ConvertBaseMetadataToBaselineQuery(%v) = %v; want %v", tc.input, result, tc.expected))
+		assert.Equal(t, len(result), len(tc.expected), fmt.Errorf("Length mismatch: got %d items, want %d items", len(result), len(tc.expected)))

Line range hint 614-622: Rename test to follow Go naming conventions.

The test name uses underscores which is not idiomatic Go. Consider using a more descriptive camelCase name.

-func TestGetLatestState_ChangeTimeLt3(t *testing.T) {
+func TestGetLatestStateWithSingleRecord(t *testing.T) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5a41d73 and ca30afa.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • common/model/model.go (2 hunks)
  • common/utils/utils.go (4 hunks)
  • common/utils/utils_test.go (10 hunks)
  • go.mod (1 hunks)
  • module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (14 hunks)
  • suite/http/mock_http_base.go (2 hunks)
  • suite/mqtt/mock_mqtt_base.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • common/model/model.go
  • go.mod
  • suite/http/mock_http_base.go
  • suite/mqtt/mock_mqtt_base.go
🧰 Additional context used
🪛 GitHub Check: codecov/patch
common/utils/utils.go

[warning] 203-203: common/utils/utils.go#L203
Added line #L203 was not covered by tests


[warning] 208-208: common/utils/utils.go#L208
Added line #L208 was not covered by tests

🔇 Additional comments (7)
module_tunnels/koupleless_mqtt_tunnel/mqtt_tunnel.go (6)

27-27: Storing context in struct field is not recommended

The previous review comment about avoiding context storage in struct fields is still valid. Contexts should be passed explicitly to functions that need them.


123-123: Use structured logging instead of string formatting

The previous review comment about using structured logging instead of fmt.Sprintf is still valid. Replace string formatting with structured fields for better log parsing and querying.

Also applies to: 147-147, 167-167, 198-198, 223-223, 248-248, 271-271


131-131: Correct usage of zaplogger.Error method

The previous review comment about correct usage of zaplogger.Error is still valid. The error should be included as a field rather than a parameter.

Also applies to: 152-152, 173-173, 189-189, 204-204, 229-229, 254-254, 277-277


282-324: Refactor command handling to reduce duplication

The previous review comment about refactoring command handling logic is still valid. The code contains significant duplication in status creation and could benefit from helper functions and constants.


340-341: Use With and structured fields for logger context

The previous review comment about using With instead of WithValues is still valid. Use structured fields to add context to logs.

Also applies to: 350-351


136-139: ⚠️ Potential issue

Ensure proper context handling in goroutine

The goroutine uses the stored context from the struct, which could lead to issues with cancellation propagation.

Pass the context explicitly to the goroutine:

-go func() {
-    <-mqttTunnel.ctx.Done()
+go func(ctx context.Context) {
+    <-ctx.Done()
     mqttTunnel.mqttClient.Disconnect()
-}()
+}(ctx)

Likely invalid or redundant comment.

common/utils/utils.go (1)

218-218: ⚠️ Potential issue

[Duplicate] Improve error logging and default port handling

This issue has been previously flagged and remains unresolved:

  • Do not pass nil as the error parameter in logger.Error; pass the actual error err.
  • Use structured logging instead of string formatting for better log parsing and consistency.
  • Consider defining and using a constant for the default port value.

Apply the following diff to address these issues:

-    		zaplogger.GetLogger().Error(nil, fmt.Sprintf("failed to parse port %s from node info", portStr))
+    		zaplogger.GetLogger().Error(err, "Failed to parse port from node info",
+    			"port", portStr,
+    			"defaultPort", defaultTunnelPort)

Define a constant for the default port at the top of the file:

+const defaultTunnelPort = 1238

And update the assignment:

-    		port = 1238
+    		port = defaultTunnelPort

Comment on lines +44 to +46
func NewMqttTunnel(ctx context.Context, env string, kubeClient client.Client, moduleDeploymentController *module_deployment_controller.ModuleDeploymentController) MqttTunnel {
return MqttTunnel{
ctx: ctx,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Constructor initializes stored context

This is related to the context storage issue. The constructor now accepts and stores a context, which could lead to issues with context cancellation and deadlines.

Consider refactoring to pass context explicitly to methods that need it, rather than storing it in the struct.

Comment on lines +203 to +208
logger.Error(deleteErr, "delete base node failed")
}
} else if apiErrors.IsNotFound(err) {
logger.Info("Node not found, skipping delete operation")
} else {
logger.WithError(err).Error("Failed to get node, cannot delete")
logger.Error(err, "Failed to get node, cannot delete")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Increase test coverage for error handling in OnBaseUnreachable

The error handling paths at lines 203 and 208 are not covered by tests, as indicated by static analysis tools. Ensuring these paths are tested is important for the reliability of your system.

Consider adding unit tests to cover these scenarios:

  • When deleting the node fails (deleteErr is not nil and not a NotFound error).
  • When fetching the node fails with an error different from NotFound.

Would you like assistance in generating the unit tests or opening a GitHub issue to track this task?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 203-203: common/utils/utils.go#L203
Added line #L203 was not covered by tests


[warning] 208-208: common/utils/utils.go#L208
Added line #L208 was not covered by tests

Comment on lines +177 to +189
func GetLatestState(records []ark.ArkBizStateRecord) (time.Time, string, string) {
latestStateTime := int64(0)
reason := ""
message := ""
for _, record := range records {
if record.State != state {
continue
}
if len(record.ChangeTime) < 3 {
continue
}
changeTime, err := time.Parse("2006-01-02 15:04:05", record.ChangeTime[:len(record.ChangeTime)-4])
if err != nil {
logrus.Errorf("failed to parse change time %s", record.ChangeTime)
continue
}
if changeTime.UnixMilli() > latestStateTime.UnixMilli() {
changeTime := record.ChangeTime
if changeTime > latestStateTime {
latestStateTime = changeTime
reason = record.Reason
message = record.Message
}
}
return latestStateTime, reason, message
return time.UnixMilli(latestStateTime), reason, message
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle empty records in GetLatestState function

If the records slice is empty, the function returns time.UnixMilli(0), which corresponds to the Unix epoch (1970-01-01). This might not be the intended behavior and could lead to incorrect timestamps elsewhere in your application.

Consider adding a check to handle empty records appropriately:

 func GetLatestState(records []ark.ArkBizStateRecord) (time.Time, string, string) {
+	if len(records) == 0 {
+		return time.Time{}, "", ""
+	}
 	latestStateTime := int64(0)
 	reason := ""
 	message := ""
 	for _, record := range records {
 		changeTime := record.ChangeTime
 		if changeTime > latestStateTime {
 			latestStateTime = changeTime
 			reason = record.Reason
 			message = record.Message
 		}
 	}
 	return time.UnixMilli(latestStateTime), reason, message
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func GetLatestState(records []ark.ArkBizStateRecord) (time.Time, string, string) {
latestStateTime := int64(0)
reason := ""
message := ""
for _, record := range records {
if record.State != state {
continue
}
if len(record.ChangeTime) < 3 {
continue
}
changeTime, err := time.Parse("2006-01-02 15:04:05", record.ChangeTime[:len(record.ChangeTime)-4])
if err != nil {
logrus.Errorf("failed to parse change time %s", record.ChangeTime)
continue
}
if changeTime.UnixMilli() > latestStateTime.UnixMilli() {
changeTime := record.ChangeTime
if changeTime > latestStateTime {
latestStateTime = changeTime
reason = record.Reason
message = record.Message
}
}
return latestStateTime, reason, message
return time.UnixMilli(latestStateTime), reason, message
func GetLatestState(records []ark.ArkBizStateRecord) (time.Time, string, string) {
if len(records) == 0 {
return time.Time{}, "", ""
}
latestStateTime := int64(0)
reason := ""
message := ""
for _, record := range records {
changeTime := record.ChangeTime
if changeTime > latestStateTime {
latestStateTime = changeTime
reason = record.Reason
message = record.Message
}
}
return time.UnixMilli(latestStateTime), reason, message
}

Copy link

@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

🧹 Outside diff range and nitpick comments (3)
cmd/module-controller/main.go (3)

89-89: Enhance error messages for better debugging

The error messages could be more descriptive by including the actual values that failed parsing.

-zlogger.Error(err, "failed to parse WORKLOAD_MAX_LEVEL, will be set to 3 default")
+zlogger.Error(err, "failed to parse WORKLOAD_MAX_LEVEL", "raw_value", utils.GetEnv("WORKLOAD_MAX_LEVEL", "3"), "default_value", 3)

-zlogger.Error(err, "failed to parse VNODE_WORKER_NUM, will be set to 8 default")
+zlogger.Error(err, "failed to parse VNODE_WORKER_NUM", "raw_value", utils.GetEnv("VNODE_WORKER_NUM", "8"), "default_value", 8)

Also applies to: 95-95


Line range hint 170-172: Fix inconsistent logging pattern

This error handling still uses the old logging pattern while the rest of the file uses zaplogger.

-		log.G(ctx).WithError(err).Error("failed to start manager")
+		zlogger.Error(err, "failed to start manager")

Line range hint 190-193: Fix inconsistent error handling in HTTP tunnel setup

The HTTP tunnel port parsing error is still using the old logging pattern.

-			log.G(ctx).WithError(err).Error("failed to parse HTTP_TUNNEL_LISTEN_PORT, set default port 7777")
+			zlogger.Error(err, "failed to parse HTTP_TUNNEL_LISTEN_PORT", "default_port", 7777)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ca30afa and 2fd5620.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (2)
  • cmd/module-controller/main.go (8 hunks)
  • go.mod (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • go.mod
🔇 Additional comments (1)
cmd/module-controller/main.go (1)

Line range hint 77-82: Remove redundant logger initialization

The code initializes both logrus and zaplogger, which creates unnecessary overhead and potential confusion.

Since we're migrating to zaplogger, the logrus initialization at line 77 should be removed:

-log.L = logruslogger.FromLogrus(logrus.NewEntry(logrus.StandardLogger()))

Comment on lines +81 to +82
zlogger := zaplogger.GetLogger()
ctx = zaplogger.WithLogger(ctx, zlogger)
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Update logger usage to use consistent zap logger pattern

The verification reveals inconsistent logger usage across the codebase:

  • Several files still use the old log.G(ctx) pattern, particularly in test files and main.go
  • The zap logger is properly initialized and propagated in most production code
  • Context propagation with zaplogger.FromContext(ctx) is consistently used in controllers and tunnels

Action items:

  • Update remaining log.G(ctx) instances in:
    • suite/http/suite_test.go
    • suite/mqtt/suite_test.go
    • suite/http/mock_http_base.go
    • suite/mqtt/mock_mqtt_base.go
    • suite/mqtt/module_mqtt_lifecycle_test.go
    • cmd/self-test/main.go
    • cmd/module-controller/main.go
🔗 Analysis chain

Verify logger configuration and performance settings

While the logger initialization and context propagation look correct, we should verify the logger's configuration and performance settings.

Also applies to: 105-106

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify logger configuration and usage across the codebase

# Check for any remaining old logger patterns
echo "Checking for remaining old logger patterns..."
rg "log\.G\(" --type go

# Check for consistent zap logger initialization
echo "Checking for consistent zap logger initialization..."
rg "zaplogger\.GetLogger\(\)" --type go

# Check for proper context propagation
echo "Checking for proper context propagation..."
rg "zaplogger\.FromContext\(ctx\)" --type go

Length of output: 4303

@lvjing2 lvjing2 merged commit 83fe1bc into main Dec 2, 2024
1 of 3 checks passed
@lvjing2 lvjing2 deleted the youji-dev branch December 2, 2024 06:30
@coderabbitai coderabbitai bot mentioned this pull request Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant