remove vm session feature
Cuts the daemon-managed guest-session machinery (start/list/show/
logs/stop/kill/attach/send). The feature shipped aimed at agent-
orchestration workflows (programmatic stdin piping into a long-lived
guest process) that aren't driving any concrete user today, and the
~2.3K LOC of daemon surface area — attach bridge, FIFO keepalive,
controller registry, sessionstream framing, SQLite persistence — was
locking in an API we'd have to keep through v0.1.0.
Anything session-flavoured that people actually need today can be
done with `vm ssh + tmux` or `vm run -- cmd`.
Deleted:
- internal/cli/commands_vm_session.go
- internal/daemon/{guest_sessions,session_lifecycle,session_attach,session_stream,session_controller}.go
- internal/daemon/session/ (guest-session helpers package)
- internal/sessionstream/ (framing package)
- internal/daemon/guest_sessions_test.go
- internal/store/guest_session_test.go
- GuestSession* types from internal/{api,model}
- Store UpsertGuestSession/GetGuestSession/ListGuestSessionsByVM/DeleteGuestSession + scanner helpers
- guest.session.* RPC dispatch entries
- 5 CLI session tests, 2 completion tests, 2 printer tests
Extracted:
- ShellQuote + FormatStepError lifted to internal/daemon/workspace/util.go
(only non-session consumer); workspace package now self-contained
- internal/daemon/guest_ssh.go keeps guestSSHClient + dialGuest +
waitForGuestSSH — still used by workspace prepare/export
- internal/daemon/fake_firecracker_test.go preserves the test helper
that used to live in guest_sessions_test.go
Store schema: CREATE TABLE guest_sessions and its column migrations
removed. Existing dev DBs keep an orphan table (harmless, pre-v0.1.0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c42fcbe012
commit
2b6437d1b4
34 changed files with 194 additions and 4031 deletions
|
|
@ -19,10 +19,7 @@ func stubCompletionSeams(
|
|||
d *deps,
|
||||
pingErr error,
|
||||
names map[string][]string,
|
||||
listErr error,
|
||||
sessions map[string][]string,
|
||||
sessionErr error,
|
||||
) {
|
||||
listErr error) {
|
||||
t.Helper()
|
||||
|
||||
d.daemonPing = func(ctx context.Context, socketPath string) (api.PingResult, error) {
|
||||
|
|
@ -37,12 +34,6 @@ func stubCompletionSeams(
|
|||
}
|
||||
return names[method], nil
|
||||
}
|
||||
d.completionSessionLister = func(ctx context.Context, socketPath, vmIDOrName string) ([]string, error) {
|
||||
if sessionErr != nil {
|
||||
return nil, sessionErr
|
||||
}
|
||||
return sessions[vmIDOrName], nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterPrefix(t *testing.T) {
|
||||
|
|
@ -82,7 +73,7 @@ func testCmdWithCtx() *cobra.Command {
|
|||
|
||||
func TestCompleteVMNamesHappyPath(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "gamma"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "gamma"}}, nil)
|
||||
|
||||
got, directive := d.completeVMNames(testCmdWithCtx(), nil, "")
|
||||
if directive != cobra.ShellCompDirectiveNoFileComp {
|
||||
|
|
@ -95,7 +86,7 @@ func TestCompleteVMNamesHappyPath(t *testing.T) {
|
|||
|
||||
func TestCompleteVMNamesDaemonDown(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, errors.New("connection refused"), nil, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, errors.New("connection refused"), nil, nil)
|
||||
|
||||
got, directive := d.completeVMNames(testCmdWithCtx(), nil, "")
|
||||
if len(got) != 0 {
|
||||
|
|
@ -108,7 +99,7 @@ func TestCompleteVMNamesDaemonDown(t *testing.T) {
|
|||
|
||||
func TestCompleteVMNamesRPCError(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, nil, errors.New("rpc failed"), nil, nil)
|
||||
stubCompletionSeams(t, d, nil, nil, errors.New("rpc failed"))
|
||||
|
||||
got, _ := d.completeVMNames(testCmdWithCtx(), nil, "")
|
||||
if len(got) != 0 {
|
||||
|
|
@ -118,7 +109,7 @@ func TestCompleteVMNamesRPCError(t *testing.T) {
|
|||
|
||||
func TestCompleteVMNamesExcludesAlreadyEntered(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "gamma"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "gamma"}}, nil)
|
||||
|
||||
got, _ := d.completeVMNames(testCmdWithCtx(), []string{"alpha"}, "")
|
||||
want := []string{"beta", "gamma"}
|
||||
|
|
@ -129,7 +120,7 @@ func TestCompleteVMNamesExcludesAlreadyEntered(t *testing.T) {
|
|||
|
||||
func TestCompleteVMNamesPrefixFilter(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "alphabet"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha", "beta", "alphabet"}}, nil)
|
||||
|
||||
got, _ := d.completeVMNames(testCmdWithCtx(), nil, "alp")
|
||||
want := []string{"alpha", "alphabet"}
|
||||
|
|
@ -140,7 +131,7 @@ func TestCompleteVMNamesPrefixFilter(t *testing.T) {
|
|||
|
||||
func TestCompleteVMNameOnlyAtPos0(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"vm.list": {"alpha"}}, nil)
|
||||
|
||||
atPos0, _ := d.completeVMNameOnlyAtPos0(testCmdWithCtx(), nil, "")
|
||||
if len(atPos0) != 1 || atPos0[0] != "alpha" {
|
||||
|
|
@ -155,7 +146,7 @@ func TestCompleteVMNameOnlyAtPos0(t *testing.T) {
|
|||
|
||||
func TestCompleteImageNames(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"image.list": {"debian-bookworm", "alpine"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"image.list": {"debian-bookworm", "alpine"}}, nil)
|
||||
|
||||
got, _ := d.completeImageNames(testCmdWithCtx(), nil, "")
|
||||
if !reflect.DeepEqual(got, []string{"debian-bookworm", "alpine"}) {
|
||||
|
|
@ -165,7 +156,7 @@ func TestCompleteImageNames(t *testing.T) {
|
|||
|
||||
func TestCompleteKernelNames(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"kernel.list": {"generic-6.12"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"kernel.list": {"generic-6.12"}}, nil)
|
||||
|
||||
got, _ := d.completeKernelNames(testCmdWithCtx(), nil, "")
|
||||
if len(got) != 1 || got[0] != "generic-6.12" {
|
||||
|
|
@ -175,58 +166,10 @@ func TestCompleteKernelNames(t *testing.T) {
|
|||
|
||||
func TestCompleteImageNameOnlyAtPos0SilentAfterFirst(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"image.list": {"alpine"}}, nil, nil, nil)
|
||||
stubCompletionSeams(t, d, nil, map[string][]string{"image.list": {"alpine"}}, nil)
|
||||
|
||||
after, _ := d.completeImageNameOnlyAtPos0(testCmdWithCtx(), []string{"alpine"}, "")
|
||||
if len(after) != 0 {
|
||||
t.Errorf("expected silence at pos 1+, got %v", after)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteSessionNames(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d,
|
||||
nil,
|
||||
map[string][]string{"vm.list": {"devbox"}},
|
||||
nil,
|
||||
map[string][]string{"devbox": {"planner", "worker"}},
|
||||
nil,
|
||||
)
|
||||
|
||||
// Position 0 → VMs.
|
||||
vms, _ := d.completeSessionNames(testCmdWithCtx(), nil, "")
|
||||
if len(vms) != 1 || vms[0] != "devbox" {
|
||||
t.Errorf("pos 0: got %v", vms)
|
||||
}
|
||||
|
||||
// Position 1 → sessions scoped to args[0].
|
||||
sessions, _ := d.completeSessionNames(testCmdWithCtx(), []string{"devbox"}, "")
|
||||
if !reflect.DeepEqual(sessions, []string{"planner", "worker"}) {
|
||||
t.Errorf("pos 1: got %v", sessions)
|
||||
}
|
||||
|
||||
// Position 1 with prefix filter.
|
||||
filtered, _ := d.completeSessionNames(testCmdWithCtx(), []string{"devbox"}, "wor")
|
||||
if len(filtered) != 1 || filtered[0] != "worker" {
|
||||
t.Errorf("pos 1 prefix: got %v", filtered)
|
||||
}
|
||||
|
||||
// Position 2+ silent.
|
||||
past, _ := d.completeSessionNames(testCmdWithCtx(), []string{"devbox", "planner"}, "")
|
||||
if len(past) != 0 {
|
||||
t.Errorf("pos 2+: got %v", past)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteSessionNamesDaemonDown(t *testing.T) {
|
||||
d := defaultDeps()
|
||||
stubCompletionSeams(t, d, errors.New("down"), nil, nil, nil, nil)
|
||||
|
||||
got, directive := d.completeSessionNames(testCmdWithCtx(), []string{"devbox"}, "")
|
||||
if len(got) != 0 {
|
||||
t.Errorf("expected no suggestions when daemon down, got %v", got)
|
||||
}
|
||||
if directive != cobra.ShellCompDirectiveNoFileComp {
|
||||
t.Errorf("directive = %d, want NoFileComp", directive)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue