Skip to content

fix: avoid vscodessh exit when server restarts #13970

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

Merged
merged 1 commit into from
Jul 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: avoid vscodessh exit when server restarts
Mitigates an issue where vscodessh would restart when the control plane
restarts, causing the entire SSH session to be reestablished.
  • Loading branch information
deansheather committed Jul 22, 2024
commit 32fe008afabfc8e52d83ffb38e60f44fe69843f5
51 changes: 42 additions & 9 deletions cli/vscodessh.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
// command via the ProxyCommand SSH option.
pid := os.Getppid()

logger := inv.Logger
// Use a stripped down writer that doesn't sync, otherwise you get
// "failed to sync sloghuman: sync /dev/stderr: The handle is
// invalid" on Windows. Syncing isn't required for stdout/stderr
// anyways.
logger := inv.Logger.AppendSinks(sloghuman.Sink(slogWriter{w: inv.Stderr})).Leveled(slog.LevelDebug)
if logDir != "" {
logFilePath := filepath.Join(logDir, fmt.Sprintf("%d.log", pid))
logFile, err := fs.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY, 0o600)
Expand All @@ -160,7 +164,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
}
dc := cliutil.DiscardAfterClose(logFile)
defer dc.Close()
logger = logger.AppendSinks(sloghuman.Sink(dc)).Leveled(slog.LevelDebug)
logger = logger.AppendSinks(sloghuman.Sink(dc))
}
if r.disableDirect {
logger.Info(ctx, "direct connections disabled")
Expand Down Expand Up @@ -204,31 +208,48 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
// command via the ProxyCommand SSH option.
networkInfoFilePath := filepath.Join(networkInfoDir, fmt.Sprintf("%d.json", pid))

statsErrChan := make(chan error, 1)
var (
firstErrTime time.Time
errCh = make(chan error, 1)
)
cb := func(start, end time.Time, virtual, _ map[netlogtype.Connection]netlogtype.Counts) {
sendErr := func(err error) {
sendErr := func(tolerate bool, err error) {
logger.Error(ctx, "collect network stats", slog.Error(err))
// Tolerate up to 1 minute of errors.
if tolerate {
if firstErrTime.IsZero() {
logger.Info(ctx, "tolerating network stats errors for up to 1 minute")
firstErrTime = time.Now()
}
if time.Since(firstErrTime) < time.Minute {
return
}
}

select {
case statsErrChan <- err:
case errCh <- err:
default:
}
}

stats, err := collectNetworkStats(ctx, agentConn, start, end, virtual)
if err != nil {
sendErr(err)
sendErr(true, err)
return
}

rawStats, err := json.Marshal(stats)
if err != nil {
sendErr(err)
sendErr(false, err)
return
}
err = afero.WriteFile(fs, networkInfoFilePath, rawStats, 0o600)
if err != nil {
sendErr(err)
sendErr(false, err)
return
}

firstErrTime = time.Time{}
}

now := time.Now()
Expand All @@ -238,7 +259,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
select {
case <-ctx.Done():
return nil
case err := <-statsErrChan:
case err := <-errCh:
return err
}
},
Expand Down Expand Up @@ -280,6 +301,18 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
return cmd
}

// slogWriter wraps an io.Writer and removes all other methods (such as Sync),
// which may cause undesired/broken behavior.
type slogWriter struct {
w io.Writer
}

var _ io.Writer = slogWriter{}

func (s slogWriter) Write(p []byte) (n int, err error) {
return s.w.Write(p)
}

type sshNetworkStats struct {
P2P bool `json:"p2p"`
Latency float64 `json:"latency"`
Expand Down
Loading