From de366ca644217ceb0a24ab1e350de69c2c3fbe62 Mon Sep 17 00:00:00 2001 From: Allen Chen Date: Fri, 7 Aug 2026 09:42:06 -0700 Subject: [PATCH] fix registered node SSH keepalives --- pkg/cmd/shell/shell.go | 14 ++++++++++++-- pkg/cmd/shell/shell_test.go | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/shell/shell.go b/pkg/cmd/shell/shell.go index 9b811c30..a9760719 100644 --- a/pkg/cmd/shell/shell.go +++ b/pkg/cmd/shell/shell.go @@ -189,8 +189,7 @@ func shellIntoExternalNode(t *terminal.Terminal, sstore ShellStore, node *nodev1 } func runSSHWithPort(target string, port int32, identityFile string) error { - sshAgentEval := `if [ -z "$SSH_AUTH_SOCK" ]; then eval $(ssh-agent -s) > /dev/null; fi` - cmd := fmt.Sprintf("%s && ssh -i %q -o StrictHostKeyChecking=no -p %d %s", sshAgentEval, identityFile, port, target) + cmd := buildSSHWithPortCommand(target, port, identityFile) sshCmd := exec.Command("bash", "-c", cmd) //nolint:gosec //cmd is constructed from API data sshCmd.Stderr = os.Stderr @@ -209,6 +208,17 @@ func runSSHWithPort(target string, port int32, identityFile string) error { return nil } +func buildSSHWithPortCommand(target string, port int32, identityFile string) string { + sshAgentEval := `if [ -z "$SSH_AUTH_SOCK" ]; then eval $(ssh-agent -s) > /dev/null; fi` + return fmt.Sprintf( + "%s && ssh -i %q -o StrictHostKeyChecking=no -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p %d %s", + sshAgentEval, + identityFile, + port, + target, + ) +} + func runSSH(sshAlias string, host bool) error { return runSSHWithOptions(sshAlias, host, true) } diff --git a/pkg/cmd/shell/shell_test.go b/pkg/cmd/shell/shell_test.go index f8d89f91..d0a5349b 100644 --- a/pkg/cmd/shell/shell_test.go +++ b/pkg/cmd/shell/shell_test.go @@ -1,6 +1,7 @@ package shell import ( + "strings" "testing" nodev1 "buf.build/gen/go/brevdev/devplane/protocolbuffers/go/devplaneapi/v1" @@ -10,6 +11,19 @@ import ( func strPtr(s string) *string { return &s } +func TestBuildSSHWithPortCommandIncludesServerKeepalives(t *testing.T) { + cmd := buildSSHWithPortCommand("ubuntu@example.com", 2222, "/tmp/test key") + + for _, option := range []string{ + "-o ServerAliveInterval=30", + "-o ServerAliveCountMax=3", + } { + if !strings.Contains(cmd, option) { + t.Errorf("buildSSHWithPortCommand() = %q, missing %q", cmd, option) + } + } +} + // TestResolveExternalNodeSSH_BuildsCorrectInfo tests that the SSH info // returned by ResolveNodeSSHEntry has the correct target, alias, and home path — // the same values shellIntoExternalNode uses to build its SSH command.