Skip to content
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

Add a ProcessManager interface for managing processes by pid file #293

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

vyasgun
Copy link
Contributor

@vyasgun vyasgun commented Apr 4, 2025

This commit does the following:

  • Adds ProcessManager interface and Process struct
  • Read and write to a pidFile specified by the user
  • Adds tests for process lifecycle with a dummy process

Summary by Sourcery

Implement a ProcessManager interface for managing processes using pid files

New Features:

  • Create a Process struct and Manager interface to handle process lifecycle management using pid files

Tests:

  • Add comprehensive test suite for process management, including process creation, finding, checking existence, and killing processes

Copy link

sourcery-ai bot commented Apr 4, 2025

Reviewer's Guide by Sourcery

This pull request introduces a ProcessManager interface and a Process struct to manage external processes by reading and writing their PIDs to a specified file. It includes functionality to find, check the existence of, and kill processes, along with comprehensive tests to ensure the correct lifecycle management of processes.

Sequence diagram for finding and killing a process

sequenceDiagram
    participant P as Process
    participant OS as Operating System

    P->>P: Pid()
    P->>OS: ReadFile(PidFilePath())
    OS-->>P: pid (string)
    P->>P: Atoi(pid)
    P->>OS: FindProcess(pid)
    OS-->>P: process, error
    alt error != nil
        P-->>P: Return error
    else
        P->>OS: Signal(SIGKILL)
        OS-->>P: error
        alt error != nil
            P-->>P: Return error
        else
            P->>OS: Wait()
            OS-->>P: _, error
            alt error != nil
                P-->>P: Return error
            else
                P-->>P: Return nil
            end
        end
    end
Loading

File-Level Changes

Change Details Files
Introduce a ProcessManager interface and its implementation, the Process struct, for managing processes via PID files.
  • Define a ProcessManager interface with methods for process lifecycle management.
  • Implement the ProcessManager interface with a Process struct.
  • Implement methods to read and write the PID to a file.
  • Implement methods to find, check existence, and kill processes.
pkg/process/process.go
Add tests for process lifecycle management.
  • Create a dummy process for testing purposes.
  • Test finding an existing process.
  • Test checking the existence of a process.
  • Test killing a process.
  • Test the scenario when the pid file contains invalid data.
pkg/process/process_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@openshift-ci openshift-ci bot requested review from anjannath and praveenkumar April 4, 2025 02:24
Copy link

openshift-ci bot commented Apr 4, 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 assign praveenkumar for approval. For more information see the Code Review Process.

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

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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @vyasgun - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a Close() method to the ProcessManager interface to handle cleanup operations like removing the pid file.
  • The New function doesn't return an error, even though the signature allows it; consider removing the error return or adding a check.
Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@vyasgun vyasgun force-pushed the pr/pid-management branch from 112f790 to 4a6f13b Compare April 4, 2025 04:42
@vyasgun
Copy link
Contributor Author

vyasgun commented Apr 4, 2025

@sourcery-ai review

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @vyasgun - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a Close() method to the Process struct to handle cleaning up resources like the pid file.
  • The Kill() method should probably remove the pid file after successfully killing the process.
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 1 issue found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +27 to +28
func New(name, pidFilePath string) (*Process, error) {
return &Process{name: name, pidFilePath: pidFilePath}, nil
Copy link

Choose a reason for hiding this comment

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

suggestion: Reconsider New() returning an error.

Since the constructor never fails, it might be cleaner to have New() simply return *Process. If error checking is anticipated in future extensions, a brief comment explaining the design decision would be helpful.

Suggested implementation:

func New(name, pidFilePath string) *Process { // Currently always succeeds; change signature if error handling is needed in the future.
	return &Process{name: name, pidFilePath: pidFilePath}

Comment on lines +39 to +45
func (p *Process) Pid() (int, error) {
data, err := os.ReadFile(p.PidFilePath())
if err != nil {
return -1, err
}
pidStr := strings.TrimSpace(string(data))
pid, err := strconv.Atoi(pidStr)
Copy link

Choose a reason for hiding this comment

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

suggestion: Wrap conversion error with more context.

While errors from strconv.Atoi are returned directly, adding contextual information (similar to the wrapping used elsewhere) could facilitate debugging if the PID file is malformed. This would improve consistency in error handling.

This commit does the following:
- Adds ProcessManager interface and Process struct
- Read and write to a pidFile specified by the user
- Adds tests for process lifecycle with a dummy process
@vyasgun vyasgun force-pushed the pr/pid-management branch from 4a6f13b to adb05d0 Compare April 4, 2025 06:46
Copy link
Collaborator

@cfergeau cfergeau left a comment

Choose a reason for hiding this comment

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

Have you tried using this to replace the equivalent code in crc, or possibly in podman? This would be the best way to make sure the interface is the one we need.

}
pidStr := strings.TrimSpace(string(data))
pid, err := strconv.Atoi(pidStr)
return pid, errors.Wrap(err, "invalid pid file")
Copy link
Collaborator

Choose a reason for hiding this comment

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

errors.Wrap is deprecated, https://github.com/pkg/errors is archived. This has been superseded by https://go.dev/blog/go1.13-errors

if err != nil {
return false
}
if err := proc.Signal(syscall.Signal(0)); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why syscall.Signal(0)? Could this use one of the SIGXXX constants from https://pkg.go.dev/syscall#pkg-constants ? (search for SIGABRT)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it safe to call SIGABRT on the running process? On searching I found sending 0 is the safest way to check a process is running

Copy link
Collaborator

Choose a reason for hiding this comment

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

I mentioned SIGABRT as a way to find the relevant section, I was not suggesting to use this signal :)
However, if FindProcess() found it, I’d assume it means the process is running, no need to check again that it’s running ?

return true
}

func (p *Process) Kill() error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We’ll probably want both Terminate and Kill as in kubernetes/minikube@10ea7ca (graceful shutdown vs forced shutdown)
But this can be added after it’s needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add Terminate as well

return p.pidFilePath
}

func (p *Process) Pid() (int, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think I’d name this ReadPidFile, or PidFromFile, as otherwise it’s easy to think it’s similar to https://pkg.go.dev/os#Getpid

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, noted.

return proc, nil
}

func (p *Process) Exists() bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Exists in kubernetes/minikube@10ea7ca has an executable argument, it’s because PIDs can be reused, and the content of the pid file can be staled, so this tries to ensure that the PID being killed is the expected process, and not some totally unrelated process which happened to be reusing the same PID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I'll take that into account and modify the function

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.

2 participants