Extract session subpackage with pure guest-session helpers
Moves the stateless parts of the guest-session subsystem into internal/daemon/session: - consts (BackendSSH, attach/transport kinds, StateRoot, LogTailLineDefault) - StateSnapshot plus ParseState / InspectStateFromDir / ApplyStateSnapshot / StateChanged - 10 on-guest path helpers (StateDir, StdoutLogPath, StdinPipePath, …) - 3 bash script generators (Script, InspectScript, SignalScript) - small utilities (ShellQuote, ExitCode, CloneStringMap, TailFileContent, ProcessAlive + syscallKill test seam, FormatStepError) - launch helpers (DefaultName, DefaultCWD, FailLaunch, NormalizeRequiredCommands, CWDPreflightScript, CommandPreflightScript, AttachInputCommand, AttachTailCommand, EnvLines) Callers inside the daemon package import the new package under the alias "sess" to avoid colliding with the local `session model.GuestSession` variables threaded through the orchestrator code. guest_sessions.go shrinks from 616 → 156 LOC; session_stream.go, session_attach.go, session_lifecycle.go, workspace.go, and guest_sessions_test.go rewire to the exported names. The orchestrator methods (StartGuestSession, BeginGuestSessionAttach, SendToGuestSession, GuestSessionLogs, refresh/inspect, sessionRegistry, guestSessionController) stay on *Daemon. Full Manager-style extraction would need prerequisite phases (operation protocol, workdisk helpers), mirroring Phase 4a's trade-off. All tests green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c13c8b11af
commit
37e02b1576
8 changed files with 612 additions and 566 deletions
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"banger/internal/api"
|
||||
sess "banger/internal/daemon/session"
|
||||
"banger/internal/guest"
|
||||
"banger/internal/model"
|
||||
"banger/internal/system"
|
||||
|
|
@ -50,34 +51,34 @@ func (d *Daemon) startGuestSessionLocked(ctx context.Context, vm model.VMRecord,
|
|||
session := model.GuestSession{
|
||||
ID: id,
|
||||
VMID: vm.ID,
|
||||
Name: defaultGuestSessionName(id, params.Command, params.Name),
|
||||
Backend: guestSessionBackendSSH,
|
||||
Name: sess.DefaultName(id, params.Command, params.Name),
|
||||
Backend: sess.BackendSSH,
|
||||
Command: params.Command,
|
||||
Args: append([]string(nil), params.Args...),
|
||||
CWD: strings.TrimSpace(params.CWD),
|
||||
Env: cloneStringMap(params.Env),
|
||||
Env: sess.CloneStringMap(params.Env),
|
||||
StdinMode: stdinMode,
|
||||
Status: model.GuestSessionStatusStarting,
|
||||
GuestStateDir: guestSessionStateDir(id),
|
||||
StdoutLogPath: guestSessionStdoutLogPath(id),
|
||||
StderrLogPath: guestSessionStderrLogPath(id),
|
||||
Tags: cloneStringMap(params.Tags),
|
||||
GuestStateDir: sess.StateDir(id),
|
||||
StdoutLogPath: sess.StdoutLogPath(id),
|
||||
StderrLogPath: sess.StderrLogPath(id),
|
||||
Tags: sess.CloneStringMap(params.Tags),
|
||||
Attachable: stdinMode == model.GuestSessionStdinPipe,
|
||||
Reattachable: stdinMode == model.GuestSessionStdinPipe,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if session.Attachable {
|
||||
session.AttachBackend = guestSessionAttachBackendSSHBridge
|
||||
session.AttachMode = guestSessionAttachModeExclusive
|
||||
session.AttachBackend = sess.AttachBackendSSHBridge
|
||||
session.AttachMode = sess.AttachModeExclusive
|
||||
} else {
|
||||
session.AttachBackend = guestSessionAttachBackendNone
|
||||
session.AttachBackend = sess.AttachBackendNone
|
||||
}
|
||||
if err := d.store.UpsertGuestSession(ctx, session); err != nil {
|
||||
return model.GuestSession{}, err
|
||||
}
|
||||
fail := func(stage, message, rawLog string) (model.GuestSession, error) {
|
||||
session = failGuestSessionLaunch(session, stage, message, rawLog)
|
||||
session = sess.FailLaunch(session, stage, message, rawLog)
|
||||
if err := d.store.UpsertGuestSession(ctx, session); err != nil {
|
||||
return model.GuestSession{}, err
|
||||
}
|
||||
|
|
@ -93,20 +94,20 @@ func (d *Daemon) startGuestSessionLocked(ctx context.Context, vm model.VMRecord,
|
|||
}
|
||||
defer client.Close()
|
||||
var preflightLog bytes.Buffer
|
||||
if err := client.RunScript(ctx, guestSessionCWDPreflightScript(session.CWD), &preflightLog); err != nil {
|
||||
return fail("preflight_cwd", fmt.Sprintf("guest working directory is unavailable: %s", defaultGuestSessionCWD(session.CWD)), preflightLog.String())
|
||||
if err := client.RunScript(ctx, sess.CWDPreflightScript(session.CWD), &preflightLog); err != nil {
|
||||
return fail("preflight_cwd", fmt.Sprintf("guest working directory is unavailable: %s", sess.DefaultCWD(session.CWD)), preflightLog.String())
|
||||
}
|
||||
preflightLog.Reset()
|
||||
requiredCommands := normalizeGuestSessionRequiredCommands(params.Command, params.RequiredCommands)
|
||||
if err := client.RunScript(ctx, guestSessionCommandPreflightScript(requiredCommands), &preflightLog); err != nil {
|
||||
requiredCommands := sess.NormalizeRequiredCommands(params.Command, params.RequiredCommands)
|
||||
if err := client.RunScript(ctx, sess.CommandPreflightScript(requiredCommands), &preflightLog); err != nil {
|
||||
return fail("preflight_command", fmt.Sprintf("required guest command is unavailable: %s", strings.TrimSpace(preflightLog.String())), preflightLog.String())
|
||||
}
|
||||
var uploadLog bytes.Buffer
|
||||
if err := client.UploadFile(ctx, guestSessionScriptPath(id), 0o755, []byte(guestSessionScript(session)), &uploadLog); err != nil {
|
||||
if err := client.UploadFile(ctx, sess.ScriptPath(id), 0o755, []byte(sess.Script(session)), &uploadLog); err != nil {
|
||||
return fail("upload_script", "upload guest session script failed", uploadLog.String())
|
||||
}
|
||||
var launchLog bytes.Buffer
|
||||
launchScript := fmt.Sprintf("set -euo pipefail\nnohup bash %s >/dev/null 2>&1 </dev/null &\ndisown || true\n", guestShellQuote(guestSessionScriptPath(id)))
|
||||
launchScript := fmt.Sprintf("set -euo pipefail\nnohup bash %s >/dev/null 2>&1 </dev/null &\ndisown || true\n", sess.ShellQuote(sess.ScriptPath(id)))
|
||||
if err := client.RunScript(ctx, launchScript, &launchLog); err != nil {
|
||||
return fail("launch", "launch guest session failed", launchLog.String())
|
||||
}
|
||||
|
|
@ -201,8 +202,8 @@ func (d *Daemon) signalGuestSession(ctx context.Context, params api.GuestSession
|
|||
}
|
||||
defer client.Close()
|
||||
var log bytes.Buffer
|
||||
if err := client.RunScript(ctx, guestSessionSignalScript(session.ID, signal), &log); err != nil {
|
||||
return model.GuestSession{}, formatGuestSessionStepError("signal guest session", err, log.String())
|
||||
if err := client.RunScript(ctx, sess.SignalScript(session.ID, signal), &log); err != nil {
|
||||
return model.GuestSession{}, sess.FormatStepError("signal guest session", err, log.String())
|
||||
}
|
||||
session.Status = model.GuestSessionStatusStopping
|
||||
session.UpdatedAt = model.Now()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue