Skip to content

Add a Process struct for managing processes by pid file #293

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

Open
wants to merge 2 commits 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 Process struct
  • Read and write to a pidFile specified by the user
  • Adds tests for process lifecycle with a sleep process

Fixes: #278
Fixes: #309

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

Summary by CodeRabbit

  • New Features

    • Added support for specifying a PID file via a new command-line flag, allowing the process ID to be written to a user-defined file.
    • Introduced process management capabilities, including checking process existence, and terminating or killing processes based on PID files.
  • Tests

    • Added comprehensive tests for PID file handling and process lifecycle management.
  • Chores

    • Updated configuration options to support the new PID file feature.

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}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good advice! the error is not needed, no reason to add it.

Copy link

Choose a reason for hiding this comment

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

Sounds good—let’s simplify the constructor. We can drop the error return and update any callers/tests accordingly. For example:

func New(name, pidFilePath string) *Process {
    return &Process{name: name, pidFilePath: pidFilePath}
}

Then:

  • Remove the error from the signature.
  • Delete the unused github.com/pkg/errors import if there are no other error-wrapping calls in that file.
  • Update any tests or code that expected two return values from New.

If you agree, go ahead and push that change. Otherwise, let me know if you see any reason to keep the error return in place for future-proofing.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It may help - need to test invalid pid with real code using the library and see if the error makes sense or we need to add some context.

Copy link

Choose a reason for hiding this comment

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

That sounds reasonable. To get a feel for whether additional context is useful, you could write a quick invalid‐PID test:

func TestPidMalformed(t *testing.T) {
    tmp := t.TempDir()
    pidFile := filepath.Join(tmp, "test.pid")
    // write something non‐numeric
    os.WriteFile(pidFile, []byte("notanumber"), 0644)

    p, _ := New("dummy", pidFile)
    _, err := p.Pid()
    if err == nil {
        t.Fatal("expected error when reading malformed PID")
    }
    t.Log("got error:", err)
}

If you run that and see something like strconv.Atoi: parsing "notanumber": invalid syntax, you’ll notice it doesn’t tell you which file failed. We could wrap it like this:

 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)
-    return pid, err
+    pid, err := strconv.Atoi(pidStr)
+    if err != nil {
+        return -1, errors.Wrapf(err, "parsing PID from file %s", p.PidFilePath())
+    }
+    return pid, nil
 }

That way the user sees both the parse error and the file path. Let me know if that addresses your concern or if you’d prefer a different error message format.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why add an old dependency (errors.Wrapf) when we can use standard library (fmt.Errorf("...: %w", err))?

Copy link

Choose a reason for hiding this comment

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

You’re absolutely right—since we’re targeting Go 1.13+ we can just use the stdlib’s fmt.Errorf with %w and drop pkg/errors entirely. For example:

-import "github.com/pkg/errors"
+import "fmt"

 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)
-    if err != nil {
-        return -1, errors.Wrapf(err, "parsing PID from file %s", p.PidFilePath())
-    }
-    return pid, nil
+    pid, err := strconv.Atoi(pidStr)
+    if err != nil {
+        return -1, fmt.Errorf("parsing PID from file %s: %w", p.PidFilePath(), err)
+    }
+    return pid, nil
 }

Then you can remove the github.com/pkg/errors import entirely. This keeps us on the standard library’s error-wrapping and avoids bringing in an extra dependency. Let me know if that works for you!

@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.

@vyasgun vyasgun force-pushed the pr/pid-management branch from adb05d0 to 5d6ee02 Compare April 23, 2025 10:08
Copy link
Collaborator

@nirs nirs left a comment

Choose a reason for hiding this comment

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

We need tests showing the expected usage of this package. You can check minikube vfkit driver to see the use cases that this package should support.

// Try to kill the non-existent process
// This should result in an error
err = managedProcess.Kill()
assert.Error(t, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Kill will be easier to use if it is idempotent. Trying to kill a process that does not exist is not an error from the caller point of view.

return pid, nil
}

func (p *Process) FindProcess() (*process.Process, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it need to be public or it an be a private helper? Exposing the underlying process package in this packages means we can never change the implementation, for example using another package to validate the process executable path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In crc, there's a codepath that uses something like FindProcess to get the underlying Process object:
crc-org/crc@main...vyasgun:crc:test-process-lib

I think this can be changed in crc so the code doesn't use the underlying process.Process object if it's not recommended to return it.

@vyasgun vyasgun force-pushed the pr/pid-management branch from 5d6ee02 to ed82a37 Compare May 27, 2025 09:06
@vyasgun vyasgun changed the title Add a ProcessManager interface for managing processes by pid file Add a Process struct for managing processes by pid file May 27, 2025
@vyasgun vyasgun requested a review from nirs May 27, 2025 09:07
@vyasgun vyasgun force-pushed the pr/pid-management branch 3 times, most recently from 4f9f7d5 to bace8df Compare June 2, 2025 11:37
@vyasgun vyasgun force-pushed the pr/pid-management branch 2 times, most recently from 7537137 to c1dc3e0 Compare June 20, 2025 07:40
@vyasgun vyasgun mentioned this pull request Jun 25, 2025
Copy link
Collaborator

@nirs nirs left a comment

Choose a reason for hiding this comment

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

I reviewed only the tests.

cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil {
return "", err
Copy link
Collaborator

Choose a reason for hiding this comment

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

This helper will be more useful if it fails the test instead of returning an error. For example:

t.Fatal(err)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe put this in process/testdata/sleep.go?

func build(t *testing.T, name string) (string, error) {
source := getSource(t, name)

out := filepath.Join(source, name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Using the t.TempDir() will make this easier to use in the tests.


out := filepath.Join(source, name)
if runtime.GOOS == "windows" {
out += ".exe"
Copy link
Collaborator

Choose a reason for hiding this comment

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

vfkit runs on Windows?

Copy link
Collaborator

Choose a reason for hiding this comment

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

vfkit only runs on macos, but could be useful if we reuse this code elsewhere.

Copy link
Collaborator

Choose a reason for hiding this comment

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

But this is test code that nobody else will use.

cmd.Env = append(os.Environ(),
"GOOS="+runtime.GOOS,
"GOARCH="+runtime.GOARCH,
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need this? vfkit is macOS only, and go build does the right thing without changing the env.


out := filepath.Join(source, name)
if runtime.GOOS == "windows" {
out += ".exe"
Copy link
Collaborator

Choose a reason for hiding this comment

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

vfkit only runs on macos, but could be useful if we reuse this code elsewhere.

)

func main() {
time.Sleep(1 * time.Minute)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wondering if we need this helper, or if we should be calling sleep directly on macos/linux, and timeout on windows

@openshift-merge-robot
Copy link
Collaborator

PR needs rebase.

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-sigs/prow repository.

Copy link

coderabbitai bot commented Jun 26, 2025

Walkthrough

This change introduces PID management capabilities to the application. It adds a --pidfile command-line option, stores the PID file path in configuration, and ensures the running process writes its PID to the specified file. A new package provides process management utilities, and comprehensive tests for PID file operations and process lifecycle are included.

Changes

File(s) Change Summary
cmd/vfkit/main.go Added logic to write the current process's PID to a specified file if the PidFile option is set.
pkg/cmdline/cmdline.go Added PidFile field to Options struct and introduced --pidfile command-line flag.
pkg/config/config.go Added optional PidFile field to VirtualMachine struct with JSON serialization support.
pkg/config/json_test.go Updated test to skip PidFile when filling VirtualMachine with non-empty values.
pkg/process/process.go Introduced new package for process management: PID file read/write, process existence, kill, etc.
pkg/process/process_test.go Added comprehensive unit tests for process management and PID file operations.
pkg/vf/testdata/sleep/sleeper.go Added a test helper Go program that sleeps for 1 minute, used in process management tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant Main
    participant ProcessPkg

    User->>CLI: Launch with --pidfile flag
    CLI->>Main: Pass PidFile option
    Main->>ProcessPkg: Create Process handler with PidFile
    Main->>ProcessPkg: Write current PID to PidFile
    ProcessPkg-->>Main: PID file written
    Main->>Main: Continue VM configuration
Loading

Assessment against linked issues

Objective Addressed Explanation
Add "pid management" code to vfkit/the client API (#278)
Add --pidfile option: allow vfkit to write its PID to a file, matching qemu behavior (#309)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Poem

A bunny with code in its paws,
Added PID files without a pause.
Now vfkit writes its number with pride,
So process managers can easily decide.
With tests that sleep and processes slain,
This rabbit ensures order will reign!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 916d08a and 9c65053.

📒 Files selected for processing (7)
  • cmd/vfkit/main.go (2 hunks)
  • pkg/cmdline/cmdline.go (2 hunks)
  • pkg/config/config.go (1 hunks)
  • pkg/config/json_test.go (1 hunks)
  • pkg/process/process.go (1 hunks)
  • pkg/process/process_test.go (1 hunks)
  • pkg/vf/testdata/sleep/sleeper.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • pkg/config/config.go
  • pkg/vf/testdata/sleep/sleeper.go
  • cmd/vfkit/main.go
  • pkg/cmdline/cmdline.go
  • pkg/config/json_test.go
  • pkg/process/process.go
  • pkg/process/process_test.go
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

This commit adds a process package with Process struct with the following methods:
- ReadPidFile() (int, error)
- Name() string
- PidFilePath() string
- Exists() (bool, error)
- Terminate() error
- Kill() error
- FindProcess() (*os.Process, error)
- WritePidFile(pid int) error

The commit also adds tests for process lifecycle with a sleep process
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

♻️ Duplicate comments (4)
pkg/process/process.go (2)

45-45: Consider using standard library error wrapping.

Based on past review comments, the codebase has moved away from github.com/pkg/errors in favor of the standard library's fmt.Errorf with %w verb.

-		return 0, fmt.Errorf("invalid pid file: %v", err)
+		return 0, fmt.Errorf("parsing PID from file %s: %w", p.PidFilePath, err)

59-61: Improve error handling robustness.

The string comparison err.Error() == "process does not exist" is fragile and could break with library updates. Consider using error type inspection or a more robust error handling approach.

-	if err != nil && err.Error() == "process does not exist" {
-		return nil, os.ErrNotExist
-	}
+	if err != nil {
+		// Handle specific error types from gopsutil more robustly
+		if isProcessNotExistError(err) {
+			return nil, os.ErrNotExist
+		}
+		return nil, err
+	}
pkg/process/process_test.go (2)

79-81: Remove unnecessary PID validation.

This PID validation is unnecessary as discussed in past comments. The conversion to int32 should happen only when needed within the process methods.

-		if pid < 0 || pid > math.MaxInt32 {
-			t.Fatal("invalid pid")
-		}

101-110: Simplify build environment setup.

As noted in past comments, vfkit is macOS-only and the explicit GOOS/GOARCH environment variables are unnecessary since Go's build system handles this correctly.

-	if runtime.GOOS == "windows" {
-		out += ".exe"
-	}
...
-	cmd.Env = append(os.Environ(),
-		"GOOS="+runtime.GOOS,
-		"GOARCH="+runtime.GOARCH,
-	)
🧹 Nitpick comments (3)
cmd/vfkit/main.go (1)

104-115: Suggest improvements for process name and missing field assignment.

The PID file writing implementation is functionally correct but has a couple of areas for improvement:

  1. Process name refinement: Using os.Args[0] as the process name may include path information. Consider using filepath.Base(os.Args[0]) for a cleaner process name.

  2. Missing configuration assignment: The vmConfig.PidFile field is never set from opts.PidFile, which seems inconsistent since the field was added to the VirtualMachine struct.

Apply this diff to address these improvements:

 	if opts.PidFile != "" {
+		vmConfig.PidFile = opts.PidFile
 		execPath, err := os.Executable()
 		if err != nil {
 			return nil, fmt.Errorf("could not determine executable path: %w", err)
 		}
-		vfProcess := process.New(os.Args[0], opts.PidFile, execPath)
+		vfProcess := process.New(filepath.Base(os.Args[0]), opts.PidFile, execPath)
 		pid := os.Getpid()
 		err = vfProcess.WritePidFile(pid)
 		if err != nil {
 			return nil, fmt.Errorf("could not write PID: %v", err)
 		}
 	}
pkg/process/process_test.go (2)

93-118: Simplify build helper function.

The build helper function can be simplified by building the test executable directly in the test's temporary directory, eliminating the need for complex path resolution.

 func build(t *testing.T, name string) (string, error) {
+	t.Helper()
 	source := getSource(t, name)
-
-	resolved, err := filepath.EvalSymlinks(t.TempDir())
-	if err != nil {
-		return "", err
-	}
-	out := filepath.Join(resolved, name)
-	if runtime.GOOS == "windows" {
-		out += ".exe"
-	}
+	out := filepath.Join(t.TempDir(), name)
 
 	t.Logf("Building %q", name)
 	cmd := exec.Command("go", "build", "-o", out, source)
-	cmd.Env = append(os.Environ(),
-		"GOOS="+runtime.GOOS,
-		"GOARCH="+runtime.GOARCH,
-	)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 
 	if err := cmd.Run(); err != nil {
 		return "", err
 	}
 	return out, nil
 }

145-153: Consider making test data location more robust.

The hardcoded path structure filepath.Join(projectRoot, "vf", "testdata") might be fragile. Consider using a more robust approach to locate test data.

 func getTestDataDir() (string, error) {
-	currentDir, err := os.Getwd()
-	if err != nil {
-		return "", err
-	}
-
-	projectRoot := filepath.Join(currentDir, "..")
-	return filepath.Join(projectRoot, "vf", "testdata"), nil
+	// Look for testdata directory starting from current directory
+	dir, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	
+	for {
+		testDataPath := filepath.Join(dir, "pkg", "vf", "testdata")
+		if _, err := os.Stat(testDataPath); err == nil {
+			return testDataPath, nil
+		}
+		
+		parent := filepath.Dir(dir)
+		if parent == dir {
+			break
+		}
+		dir = parent
+	}
+	
+	return "", fmt.Errorf("testdata directory not found")
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d196497 and 916d08a.

📒 Files selected for processing (7)
  • cmd/vfkit/main.go (2 hunks)
  • pkg/cmdline/cmdline.go (2 hunks)
  • pkg/config/config.go (1 hunks)
  • pkg/config/json_test.go (1 hunks)
  • pkg/process/process.go (1 hunks)
  • pkg/process/process_test.go (1 hunks)
  • pkg/vf/testdata/sleep/sleeper.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
cmd/vfkit/main.go (1)
pkg/process/process.go (1)
  • New (35-37)
pkg/process/process_test.go (1)
pkg/process/process.go (2)
  • New (35-37)
  • Process (29-33)
🔇 Additional comments (12)
pkg/vf/testdata/sleep/sleeper.go (1)

1-9: LGTM! Simple and effective test helper.

The sleeper program serves its purpose well as a test utility for process lifecycle management tests. The implementation is clean and straightforward.

pkg/config/config.go (1)

35-35: LGTM! Clean field addition with proper JSON tagging.

The PidFile field is properly added to the VirtualMachine struct with appropriate JSON tagging for optional serialization.

pkg/config/json_test.go (1)

234-234: LGTM! Appropriate test exclusion for the new field.

Adding "PidFile" to the skipFields list is correct for JSON stability testing, preventing arbitrary test data from being set in this configuration-specific field.

pkg/cmdline/cmdline.go (2)

31-31: LGTM! Consistent field addition.

The PidFile field addition follows the existing pattern in the Options struct.


61-61: LGTM! Proper command line flag implementation.

The --pidfile flag is correctly implemented following the established pattern with appropriate StringVar binding.

cmd/vfkit/main.go (1)

38-38: LGTM! Appropriate import addition.

The process package import is correctly added to support the new PID file functionality.

pkg/process/process.go (2)

35-37: LGTM: Constructor implementation is clean.

The constructor follows the past review feedback by removing the unnecessary error return and keeping it simple.


117-119: LGTM: PID file writing with appropriate permissions.

Writing the PID file with 0600 permissions is appropriate for security.

pkg/process/process_test.go (4)

17-27: LGTM: Basic PID file operations test.

The test correctly validates basic PID file read/write functionality using t.TempDir() for isolation.


29-34: LGTM: Missing PID file test.

Good test coverage for handling missing PID files with appropriate error checking.


36-45: LGTM: Invalid PID file test.

Properly tests error handling for invalid PID file content.


130-143: LGTM: Process startup helper with proper cleanup.

Good use of t.Cleanup() to ensure processes are properly terminated after tests complete.

Introduce a new --pidfile flag to optionally specify a file where the
running vfkit process writes its PID. This allows easier process
management and monitoring.
@vyasgun vyasgun force-pushed the pr/pid-management branch from 916d08a to 9c65053 Compare June 26, 2025 11:24
Copy link

openshift-ci bot commented Jun 26, 2025

@vyasgun: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/build 9c65053 link true /test build

Full PR test history. Your PR dashboard.

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-sigs/prow repository. I understand the commands that are listed here.

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.

Add --pidfile option Add "pid management" code to vfkit/the client API?
4 participants