daemon split (3/5): extract *WorkspaceService service

Third phase of splitting the daemon god-struct. WorkspaceService now
owns workspace.prepare / workspace.export plus the ssh-key +
git-identity + arbitrary-file sync that runs as part of VM start's
prepare_work_disk capability hook. workspaceLocks (the per-VM tar
serialisation set) lives on the service.

workspace.go and vm_authsync.go flipped receivers from *Daemon to
*WorkspaceService. The workspaceInspectRepo / workspaceImport test
seams moved onto the service as fields.

Peer-service dependencies go through narrow function-typed fields:
vmResolver, aliveChecker, waitGuestSSH, dialGuest, imageResolver,
imageWorkSeed, withVMLockByRef, beginOperation. WorkspaceService
never touches VMService / HostNetwork / ImageService directly —
only the exact operations the Daemon hands it at construction.

Daemon lazy-init helper workspaceSvc() mirrors the Phase 1/2
pattern. Test literals still write `&Daemon{store: db, runner: r}`
and get a wired workspace service for free. Tests that override the
inspect/import seams (workspace_test.go, ~4 sites) assign them on
d.workspaceSvc() instead of on the daemon literal.

Dispatch in daemon.go: vm.workspace.prepare and vm.workspace.export
now forward one-liners to d.workspaceSvc().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thales Maciel 2026-04-20 20:42:31 -03:00
parent d7614a3b2b
commit c0d456e734
No known key found for this signature in database
GPG key ID: 33112E6833C34679
8 changed files with 202 additions and 94 deletions

View file

@ -811,7 +811,7 @@ func TestEnsureAuthorizedKeyOnWorkDiskRepairsNestedRootLayout(t *testing.T) {
vm := testVM("seed-repair", "image-seed-repair", "172.16.0.61")
vm.Runtime.WorkDiskPath = workDiskDir
if err := d.ensureAuthorizedKeyOnWorkDisk(context.Background(), &vm, model.Image{}, workDiskPreparation{}); err != nil {
if err := d.workspaceSvc().ensureAuthorizedKeyOnWorkDisk(context.Background(), &vm, model.Image{}, workDiskPreparation{}); err != nil {
t.Fatalf("ensureAuthorizedKeyOnWorkDisk: %v", err)
}
if _, err := os.Stat(filepath.Join(workDiskDir, "root")); !os.IsNotExist(err) {
@ -848,7 +848,7 @@ func TestEnsureGitIdentityOnWorkDiskCopiesHostGlobalIdentity(t *testing.T) {
vm := testVM("git-identity", "image-git-identity", "172.16.0.67")
vm.Runtime.WorkDiskPath = workDiskDir
if err := d.ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
t.Fatalf("ensureGitIdentityOnWorkDisk: %v", err)
}
@ -881,7 +881,7 @@ func TestEnsureGitIdentityOnWorkDiskPreservesExistingGuestConfig(t *testing.T) {
vm := testVM("git-identity-preserve", "image-git-identity", "172.16.0.68")
vm.Runtime.WorkDiskPath = workDiskDir
if err := d.ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
t.Fatalf("ensureGitIdentityOnWorkDisk: %v", err)
}
@ -925,7 +925,7 @@ func TestEnsureGitIdentityOnWorkDiskWarnsAndSkipsWhenHostIdentityIncomplete(t *t
vm := testVM("git-identity-missing", "image-git-identity", "172.16.0.69")
vm.Runtime.WorkDiskPath = workDiskDir
if err := d.ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().ensureGitIdentityOnWorkDisk(context.Background(), &vm); err != nil {
t.Fatalf("ensureGitIdentityOnWorkDisk: %v", err)
}
@ -951,7 +951,7 @@ func TestEnsureGitIdentityOnWorkDiskWarnsAndSkipsWhenHostIdentityIncomplete(t *t
func TestRunFileSyncNoOpWhenConfigEmpty(t *testing.T) {
d := &Daemon{runner: &filesystemRunner{t: t}}
vm := testVM("no-sync", "image", "172.16.0.70")
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}
}
@ -979,7 +979,7 @@ func TestRunFileSyncCopiesFile(t *testing.T) {
}
vm := testVM("sync-file", "image", "172.16.0.71")
vm.Runtime.WorkDiskPath = workDisk
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}
@ -1019,7 +1019,7 @@ func TestRunFileSyncRespectsCustomMode(t *testing.T) {
}
vm := testVM("sync-mode", "image", "172.16.0.72")
vm.Runtime.WorkDiskPath = workDisk
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}
@ -1054,7 +1054,7 @@ func TestRunFileSyncSkipsMissingHostPath(t *testing.T) {
}
vm := testVM("sync-missing", "image", "172.16.0.73")
vm.Runtime.WorkDiskPath = workDisk
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}
@ -1093,7 +1093,7 @@ func TestRunFileSyncOverwritesExistingGuestFile(t *testing.T) {
}
vm := testVM("sync-overwrite", "image", "172.16.0.74")
vm.Runtime.WorkDiskPath = workDisk
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}
@ -1135,7 +1135,7 @@ func TestRunFileSyncCopiesDirectoryRecursively(t *testing.T) {
}
vm := testVM("sync-dir", "image", "172.16.0.75")
vm.Runtime.WorkDiskPath = workDisk
if err := d.runFileSync(context.Background(), &vm); err != nil {
if err := d.workspaceSvc().runFileSync(context.Background(), &vm); err != nil {
t.Fatalf("runFileSync: %v", err)
}