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.
|
// restartBdDaemons restarts all bd daemons.
|
||||||
func restartBdDaemons() error { //nolint:unparam // error return kept for future use
|
func restartBdDaemons() error { //nolint:unparam // error return kept for future use
|
||||||
// Stop all daemons first
|
// Stop all daemons first using pkill to avoid auto-start side effects
|
||||||
stopCmd := exec.Command("bd", "daemon", "killall")
|
_ = exec.Command("pkill", "-TERM", "-f", "bd daemon").Run()
|
||||||
_ = stopCmd.Run() // Ignore errors - daemons might not be running
|
|
||||||
|
|
||||||
// Give time for cleanup
|
// Give time for cleanup
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
@@ -159,39 +158,20 @@ func StopAllBdProcesses(dryRun, force bool) (int, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CountBdDaemons returns count of running bd daemons.
|
// 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 {
|
func CountBdDaemons() int {
|
||||||
listCmd := exec.Command("bd", "daemon", "list", "--json")
|
// Use pgrep -f with wc -l for cross-platform compatibility
|
||||||
output, err := listCmd.Output()
|
// (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 {
|
if err != nil {
|
||||||
return 0
|
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) {
|
func stopBdDaemons(force bool) (int, int) {
|
||||||
before := CountBdDaemons()
|
before := CountBdDaemons()
|
||||||
@@ -199,19 +179,11 @@ func stopBdDaemons(force bool) (int, int) {
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
killCmd := exec.Command("bd", "daemon", "killall")
|
// Use pkill directly instead of "bd daemon killall" to avoid triggering
|
||||||
_ = killCmd.Run()
|
// daemon auto-start as a side effect of running bd commands.
|
||||||
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
after := CountBdDaemons()
|
|
||||||
if after == 0 {
|
|
||||||
return before, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: pkill -f pattern may match unintended processes in rare cases
|
// Note: pkill -f pattern may match unintended processes in rare cases
|
||||||
// (e.g., editors with "bd daemon" in file content). This is acceptable
|
// (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 {
|
if force {
|
||||||
_ = exec.Command("pkill", "-9", "-f", "bd daemon").Run()
|
_ = exec.Command("pkill", "-9", "-f", "bd daemon").Run()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,46 +5,6 @@ import (
|
|||||||
"testing"
|
"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) {
|
func TestCountBdActivityProcesses(t *testing.T) {
|
||||||
count := CountBdActivityProcesses()
|
count := CountBdActivityProcesses()
|
||||||
if count < 0 {
|
if count < 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user