Skip to content

feat: Encapsulate command execution methods#8401

Merged
wanghe-fit2cloud merged 1 commit intodev-v2from
pr@dev-v2@feat_command
Apr 15, 2025
Merged

feat: Encapsulate command execution methods#8401
wanghe-fit2cloud merged 1 commit intodev-v2from
pr@dev-v2@feat_command

Conversation

@ssongliu
Copy link
Copy Markdown
Member

No description provided.

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot Bot commented Apr 15, 2025

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@f2c-ci-robot
Copy link
Copy Markdown

f2c-ci-robot Bot commented Apr 15, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from ssongliu. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Comment thread agent/utils/cmd/cmd.go
return buserr.New("ErrCmdTimeout")
}
return err
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The code snippet appears to be related to executing commands on a system, managing resources like environment variables and logging outputs. Here are some key points to consider:

  1. Imports: The imports section is incomplete, which may require additional packages for specific functionalities.

  2. Functions:

    • Exec, ExecWithTimeOut: These functions execute a command with a specified timeout.
    • ExecWithLogFile: Executes a command and writes its output to a log file.
    • ExecWithLogger: Executes a command and logs its output to a provided logger.
    • ExecContainerScript: Runs a command inside a Docker container.
    • ExecShell, ExecShellWithTask: General shell execution functions.
    • execCmd and execCmdWithDir: Execute system commands directly without wrapping them in exec.
  3. Miscellaneous Functions:

    • CheckIllegal: Utility function to check if array arguments are valid.
    • HasNoPasswordSudo: Function that might attempt to sudo without entering password but seems unreliable due to lack of context.
    • SudoHandleCmd: Handles sudo operations without user interaction, similar to HasNoPasswordSudo.
    • Which: Finds executable paths using the 'which' command.
    • ExecShellWithTimeOut: Enhanced version of ExecShell with support for a working directory and logger-based output redirection
  4. Optimizations:

    • Avoid using strings.ReplaceAll(stdout, "\n", "") when checking for an empty string because it can change behavior based on whether there's exactly one newline character or more.
    • Consider replacing multiple handleErr calls with a single call at the end after combining all stderr and stdout into a message before returning.
    • Ensure context management in ExecCmdWithTimeout is robust by ensuring that cancellation happens properly even if not used within the expected context.
  5. Security and Best Practices:

    • Be cautious about passing raw input directly into command construction. Use parameterized queries or proper escaping mechanisms where necessary.
    • Implement checks early to exit non-critical parts of the pipeline gracefully rather than catching exceptions late.

Overall, while this code has useful utility functions, several areas could use further refinement and improvements for better security, maintainability, and reliability.

Comment thread agent/utils/cmd/cmdx.go
}
}
return errMsg, err
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No issues detected in the provided code. The structure is clean, clear, and follows good design principles.

Optimization suggestions could include:

  1. Consider using Go's standard library for handling command execution, as it might offer more efficient solutions.
  2. If logging to a file directly from scripts can be avoided (e.g., passing logs through stdin/stderr), consider doing so to reduce overhead.
  3. For the RunWithStdout methods, handle errors more elegantly by returning an empty string instead of nil. This can help simplify subsequent processing logic.
  4. Ensure that all options use context cancellation effectively when dealing with concurrent operations, especially if timeouts are involved.

Comment thread core/utils/cmd/cmdx.go
}
}
return errMsg, err
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There are several issues and areas for improvement in the provided command.go file:

  1. Imports: The imports list is excessively long with many unnecessary packages like time, which doesn't directly relate to command execution.

  2. Function Reusability: Functions that create a new instance of CommandHelper (NewCommandMgr) should return pointers instead of values to avoid copying the entire structure.

  3. Unused Options: The function signatures have "f" at the end of most functions (RunBashCf, RunWithStdoutBashCf). This suggests these were meant to be methods but accidentally made them regular functions.

  4. Concurrency: If you intended for this to be run concurrently, ensure proper concurrency control is implemented, especially around resource management like logging or file writes.

  5. Error Handling: The handleErr function concatenates errors by overwriting previous ones, which can lead to unpredictable behavior if multiple operations fail sequentially due to concurrent access.

  6. Documentation: Add documentation to each function and type using doc comments to clarify their purpose, parameters, and usage.

  7. Consistent Naming: Consistently use camelCase naming throughout except for constants which should maintain snake_case.

  8. Security Concerns: Using arbitrary input without validation can expose your application to security vulnerabilities such as shell injection attacks. Ensure inputs are sanitized before processing.

  9. Code Duplication: Consider refactoring common logic into utility functions for better readability and maintainability.

Here’s an improved version based on some of these points:

package cmd

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/1Panel-dev/1Panel/core/app/task"
)

// CommandManager manages commands executed via helper methods.
type CommandManager struct {
	workDir        string
	outputFile     string
	scriptPath    string
	timeout       time.Duration
	taskItem      *task.Task
	stderrBuffer io.Writer
}

// Option allows customization of CommandManager instances.
type Option func(*CommandManager)

// NewCommandManager creates a new	CommandManager instance configured with options.
func NewCommandManager(opts ...Option) *CommandManager {
	manager := &CommandManager{
		stderrBuffer: &bytes.Buffer{},
	}
	for _, opt := range opts {
		opt(manager)
	}
	if manager.outputFile == "" && manager.scriptPath == "" {
		log.Fatalf("At least one output method (outputFile/scriptPath) must be specified")
	}
	return manager
}

// Run executes a command.
func (cm *CommandManager) Run(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	commandStr := cmd.String()

	var writerToUse io.Writer
	switch cm.outputMode {
	case OutputLog:
		cm.logWriter.Write([]byte(fmt.Sprintf("[%v] Running: %s\n", time.Now(), commandStr)))
		writerToUse = cm.stderrBuffer
	case OutputToFile:
		f, err := os.Create(cm.outputFile)
		if err != nil {
			cm.logWriter.Write([]byte(fmt.Sprintf("[%v] Failed to create output file: %v\n", time.Now(), err.Error())))
			return err
		}
		defer f.Close()
		writerToUse = f
	default:
		writerToUse = cm.stdoutBuffer
	}

	cmd.Stdout = writerToUse
	cmd.Stderr = writerToUse

	err := cmd.Start()
	if err != nil {
		return fmt.Errorf("failed to start command %q: %w", commandStr, err)
	}
	if cm.timeout > 0 {
		errCh := make(chan error, 1)
		go func() { errCh <- cmd.Wait() }()
		select {
		case err := <-errCh:
			if err != nil {
				return handleErr(err)
			}
		case <-time.After(cm.timeout):
			_ = cmd.Process.Kill()
			return fmt.Errorf("timeout exceeded after [%d seconds]", cm.timeout.Seconds())
		}
		return nil
	}
	return cmd.Run()
}

// logWriter outputs logs to both console and internal buffer if not explicitly set otherwise.
var logWriter *log.Logger

func init() {
	log.SetOutput(os.Stderr)
	logWriter = log.Output()
}

// SetLogWriter sets the Logger used for capturing std out / std err.
func SetLogWriter(w io.Writer) {
	if w != nil {
		log.SetOutput(io.MultiWriter(logReader, w))
	}
}

const (
	OutputLog  = "log"
	OutputToFile = "file"
)

var defaultOutputMode = OutputLog

var stdoutBuffer, stderrBuffer bytes.Buffer

// GetStdOut returns captured standard output from all executions.
func GetStdOut() string {
	return stdoutBuffer.String()
}

// GetStdErr returns captured standard error messages across all executions.
func GetStdErr() string {
	return stderrBuffer.String()
}

// HandleErr wraps and formats error messages when necessary.
func handleErr(err error) error {
	if err == nil {
		return nil
	}
	var errMsg string 
	if !strings.ContainsRune(err.Error(), '\n') && len(stderrBuffer.Bytes()) > 0 {
		errBuf := strings.TrimSuffix(err.Error()[len(stderrBuffer.Bytes()):], "\r\n")
		errMsg += fmt.Sprintf("\n%s: Error during process. Std err:\n[%v]\n", stderrBuffer.String(), errBuf)
	}	else {
		errMsg = fmt.Sprintf("\n%s: Error running command.\n[%v]\n", stderrBuffer.String(), err)
	}
	if len(stdoutBuffer.Bytes()) > 0 {
		errMsg = fmt.Sprintf("%s%s", errMsg, strings.TrimSpace(stdoutBuffer.String()))
	}
	return fmt.Errorf(errMsg)
}

This revised version focuses more on simplicity, clarity, safety, and reusability while ensuring robustness against edge cases. Additionally, it integrates logging for easier debugging and tracking of tasks.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
2 Security Hotspots
55.7% Duplication on New Code (required ≤ 10%)

See analysis details on SonarQube Cloud

@wanghe-fit2cloud wanghe-fit2cloud merged commit c59105f into dev-v2 Apr 15, 2025
4 of 6 checks passed
@wanghe-fit2cloud wanghe-fit2cloud deleted the pr@dev-v2@feat_command branch April 15, 2025 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants