fix: prevent gt down --all from respawning bd daemon (#457)
CountBdDaemons() was using `bd daemon list --json` which triggers daemon auto-start as a side effect. During shutdown verification, this caused a new daemon to spawn after all daemons were killed, resulting in "bd daemon shutdown incomplete: 1 still running" error. Replaced all `bd daemon killall` calls with pkill in: - stopBdDaemons() - restartBdDaemons() Changed CountBdDaemons() to use pgrep instead of bd daemon list. Also removed the now-unused parseBdDaemonCount helper function and its tests.
This commit is contained in:
@@ -109,9 +109,8 @@ func EnsureBdDaemonHealth(workDir string) string {
|
||||
|
||||
// restartBdDaemons restarts all bd daemons.
|
||||
func restartBdDaemons() error { //nolint:unparam // error return kept for future use
|
||||
// Stop all daemons first
|
||||
stopCmd := exec.Command("bd", "daemon", "killall")
|
||||
_ = stopCmd.Run() // Ignore errors - daemons might not be running
|
||||
// Stop all daemons first using pkill to avoid auto-start side effects
|
||||
_ = exec.Command("pkill", "-TERM", "-f", "bd daemon").Run()
|
||||
|
||||
// Give time for cleanup
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
@@ -159,39 +158,20 @@ func StopAllBdProcesses(dryRun, force bool) (int, int, error) {
|
||||
}
|
||||
|
||||
// CountBdDaemons returns count of running bd daemons.
|
||||
// Uses pgrep instead of "bd daemon list" to avoid triggering daemon auto-start
|
||||
// during shutdown verification.
|
||||
func CountBdDaemons() int {
|
||||
listCmd := exec.Command("bd", "daemon", "list", "--json")
|
||||
output, err := listCmd.Output()
|
||||
// Use pgrep -f with wc -l for cross-platform compatibility
|
||||
// (macOS pgrep doesn't support -c flag)
|
||||
cmd := exec.Command("sh", "-c", "pgrep -f 'bd daemon' 2>/dev/null | wc -l")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return parseBdDaemonCount(output)
|
||||
count, _ := strconv.Atoi(strings.TrimSpace(string(output)))
|
||||
return count
|
||||
}
|
||||
|
||||
// parseBdDaemonCount parses bd daemon list --json output.
|
||||
func parseBdDaemonCount(output []byte) int {
|
||||
if len(output) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var daemons []any
|
||||
if err := json.Unmarshal(output, &daemons); err == nil {
|
||||
return len(daemons)
|
||||
}
|
||||
|
||||
var wrapper struct {
|
||||
Daemons []any `json:"daemons"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
if err := json.Unmarshal(output, &wrapper); err == nil {
|
||||
if wrapper.Count > 0 {
|
||||
return wrapper.Count
|
||||
}
|
||||
return len(wrapper.Daemons)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func stopBdDaemons(force bool) (int, int) {
|
||||
before := CountBdDaemons()
|
||||
@@ -199,19 +179,11 @@ func stopBdDaemons(force bool) (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
killCmd := exec.Command("bd", "daemon", "killall")
|
||||
_ = killCmd.Run()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
after := CountBdDaemons()
|
||||
if after == 0 {
|
||||
return before, 0
|
||||
}
|
||||
|
||||
// Use pkill directly instead of "bd daemon killall" to avoid triggering
|
||||
// daemon auto-start as a side effect of running bd commands.
|
||||
// Note: pkill -f pattern may match unintended processes in rare cases
|
||||
// (e.g., editors with "bd daemon" in file content). This is acceptable
|
||||
// as a fallback when bd daemon killall fails.
|
||||
// given the alternative of respawning daemons during shutdown.
|
||||
if force {
|
||||
_ = exec.Command("pkill", "-9", "-f", "bd daemon").Run()
|
||||
} else {
|
||||
|
||||
@@ -5,46 +5,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseBdDaemonCount_Array(t *testing.T) {
|
||||
input := []byte(`[{"pid":1234},{"pid":5678}]`)
|
||||
count := parseBdDaemonCount(input)
|
||||
if count != 2 {
|
||||
t.Errorf("expected 2, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBdDaemonCount_ObjectWithCount(t *testing.T) {
|
||||
input := []byte(`{"count":3,"daemons":[{},{},{}]}`)
|
||||
count := parseBdDaemonCount(input)
|
||||
if count != 3 {
|
||||
t.Errorf("expected 3, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBdDaemonCount_ObjectWithDaemons(t *testing.T) {
|
||||
input := []byte(`{"daemons":[{},{}]}`)
|
||||
count := parseBdDaemonCount(input)
|
||||
if count != 2 {
|
||||
t.Errorf("expected 2, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBdDaemonCount_Empty(t *testing.T) {
|
||||
input := []byte(``)
|
||||
count := parseBdDaemonCount(input)
|
||||
if count != 0 {
|
||||
t.Errorf("expected 0, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseBdDaemonCount_Invalid(t *testing.T) {
|
||||
input := []byte(`not json`)
|
||||
count := parseBdDaemonCount(input)
|
||||
if count != 0 {
|
||||
t.Errorf("expected 0 for invalid JSON, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountBdActivityProcesses(t *testing.T) {
|
||||
count := CountBdActivityProcesses()
|
||||
if count < 0 {
|
||||
|
||||
Reference in New Issue
Block a user