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:
parent
d7614a3b2b
commit
c0d456e734
8 changed files with 202 additions and 94 deletions
|
|
@ -23,8 +23,8 @@ type gitIdentity struct {
|
|||
Email string
|
||||
}
|
||||
|
||||
func (d *Daemon) ensureAuthorizedKeyOnWorkDisk(ctx context.Context, vm *model.VMRecord, image model.Image, prep workDiskPreparation) error {
|
||||
fingerprint, err := guest.AuthorizedPublicKeyFingerprint(d.config.SSHKeyPath)
|
||||
func (s *WorkspaceService) ensureAuthorizedKeyOnWorkDisk(ctx context.Context, vm *model.VMRecord, image model.Image, prep workDiskPreparation) error {
|
||||
fingerprint, err := guest.AuthorizedPublicKeyFingerprint(s.config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("derive authorized ssh key fingerprint: %w", err)
|
||||
}
|
||||
|
|
@ -32,18 +32,18 @@ func (d *Daemon) ensureAuthorizedKeyOnWorkDisk(ctx context.Context, vm *model.VM
|
|||
vmCreateStage(ctx, "prepare_work_disk", "using seeded SSH access")
|
||||
return nil
|
||||
}
|
||||
publicKey, err := guest.AuthorizedPublicKey(d.config.SSHKeyPath)
|
||||
publicKey, err := guest.AuthorizedPublicKey(s.config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("derive authorized ssh key: %w", err)
|
||||
}
|
||||
vmCreateStage(ctx, "prepare_work_disk", "provisioning SSH access on work disk")
|
||||
workMount, cleanupWork, err := system.MountTempDir(ctx, d.runner, vm.Runtime.WorkDiskPath, false)
|
||||
workMount, cleanupWork, err := system.MountTempDir(ctx, s.runner, vm.Runtime.WorkDiskPath, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanupWork()
|
||||
|
||||
if err := d.flattenNestedWorkHome(ctx, workMount); err != nil {
|
||||
if err := flattenNestedWorkHome(ctx, s.runner, workMount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -51,23 +51,23 @@ func (d *Daemon) ensureAuthorizedKeyOnWorkDisk(ctx context.Context, vm *model.VM
|
|||
// mounts at /root, which sshd inspects when StrictModes is on (the
|
||||
// default after the hardening drop-in). Any drift — owner != root,
|
||||
// group/other-writable — would make sshd silently reject the key.
|
||||
if err := normaliseHomeDirPerms(ctx, d.runner, workMount); err != nil {
|
||||
if err := normaliseHomeDirPerms(ctx, s.runner, workMount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sshDir := filepath.Join(workMount, ".ssh")
|
||||
if _, err := d.runner.RunSudo(ctx, "mkdir", "-p", sshDir); err != nil {
|
||||
if _, err := s.runner.RunSudo(ctx, "mkdir", "-p", sshDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := d.runner.RunSudo(ctx, "chmod", "700", sshDir); err != nil {
|
||||
if _, err := s.runner.RunSudo(ctx, "chmod", "700", sshDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := d.runner.RunSudo(ctx, "chown", "0:0", sshDir); err != nil {
|
||||
if _, err := s.runner.RunSudo(ctx, "chown", "0:0", sshDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authorizedKeysPath := filepath.Join(sshDir, "authorized_keys")
|
||||
existing, err := d.runner.RunSudo(ctx, "cat", authorizedKeysPath)
|
||||
existing, err := s.runner.RunSudo(ctx, "cat", authorizedKeysPath)
|
||||
if err != nil {
|
||||
existing = nil
|
||||
}
|
||||
|
|
@ -89,12 +89,12 @@ func (d *Daemon) ensureAuthorizedKeyOnWorkDisk(ctx context.Context, vm *model.VM
|
|||
}
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if _, err := d.runner.RunSudo(ctx, "install", "-m", "600", tmpPath, authorizedKeysPath); err != nil {
|
||||
if _, err := s.runner.RunSudo(ctx, "install", "-m", "600", tmpPath, authorizedKeysPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if prep.ClonedFromSeed && image.Managed {
|
||||
vmCreateStage(ctx, "prepare_work_disk", "refreshing managed work seed")
|
||||
if err := d.imageSvc().refreshManagedWorkSeedFingerprint(ctx, image, fingerprint); err != nil {
|
||||
if err := s.imageWorkSeed(ctx, image, fingerprint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -120,15 +120,15 @@ func normaliseHomeDirPerms(ctx context.Context, runner system.CommandRunner, wor
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Daemon) ensureGitIdentityOnWorkDisk(ctx context.Context, vm *model.VMRecord) error {
|
||||
runner := d.runner
|
||||
func (s *WorkspaceService) ensureGitIdentityOnWorkDisk(ctx context.Context, vm *model.VMRecord) error {
|
||||
runner := s.runner
|
||||
if runner == nil {
|
||||
runner = system.NewRunner()
|
||||
}
|
||||
|
||||
identity, err := resolveHostGlobalGitIdentity(ctx, runner)
|
||||
if err != nil {
|
||||
d.warnGitIdentitySyncSkipped(*vm, hostGlobalGitIdentitySource, err)
|
||||
s.warnGitIdentitySyncSkipped(*vm, hostGlobalGitIdentitySource, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ func (d *Daemon) ensureGitIdentityOnWorkDisk(ctx context.Context, vm *model.VMRe
|
|||
}
|
||||
defer cleanupWork()
|
||||
|
||||
if err := d.flattenNestedWorkHome(ctx, workMount); err != nil {
|
||||
if err := flattenNestedWorkHome(ctx, s.runner, workMount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -155,12 +155,12 @@ func (d *Daemon) ensureGitIdentityOnWorkDisk(ctx context.Context, vm *model.VMRe
|
|||
// Directory entries: walked in Go — each file is installed with its
|
||||
// source permissions, each subdir is mkdir'd. The entry's `mode`
|
||||
// field is only honoured for file entries.
|
||||
func (d *Daemon) runFileSync(ctx context.Context, vm *model.VMRecord) error {
|
||||
if len(d.config.FileSync) == 0 {
|
||||
func (s *WorkspaceService) runFileSync(ctx context.Context, vm *model.VMRecord) error {
|
||||
if len(s.config.FileSync) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
runner := d.runner
|
||||
runner := s.runner
|
||||
if runner == nil {
|
||||
runner = system.NewRunner()
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ func (d *Daemon) runFileSync(ctx context.Context, vm *model.VMRecord) error {
|
|||
}
|
||||
workMount = m
|
||||
cleanupWork = c
|
||||
if err := d.flattenNestedWorkHome(ctx, workMount); err != nil {
|
||||
if err := flattenNestedWorkHome(ctx, s.runner, workMount); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return workMount, nil
|
||||
|
|
@ -194,14 +194,14 @@ func (d *Daemon) runFileSync(ctx context.Context, vm *model.VMRecord) error {
|
|||
}
|
||||
}()
|
||||
|
||||
for _, entry := range d.config.FileSync {
|
||||
for _, entry := range s.config.FileSync {
|
||||
hostPath := expandHostPath(entry.Host, hostHome)
|
||||
guestRel := guestPathRelativeToRoot(entry.Guest)
|
||||
|
||||
info, err := os.Stat(hostPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
d.warnFileSyncSkipped(*vm, hostPath, err)
|
||||
s.warnFileSyncSkipped(*vm, hostPath, err)
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("file_sync: stat %s: %w", hostPath, err)
|
||||
|
|
@ -365,18 +365,18 @@ func writeGitIdentity(ctx context.Context, runner system.CommandRunner, gitConfi
|
|||
return err
|
||||
}
|
||||
|
||||
func (d *Daemon) warnFileSyncSkipped(vm model.VMRecord, hostPath string, err error) {
|
||||
if d.logger == nil || err == nil {
|
||||
func (s *WorkspaceService) warnFileSyncSkipped(vm model.VMRecord, hostPath string, err error) {
|
||||
if s.logger == nil || err == nil {
|
||||
return
|
||||
}
|
||||
d.logger.Warn("file_sync skipped", append(vmLogAttrs(vm), "host_path", hostPath, "error", err.Error())...)
|
||||
s.logger.Warn("file_sync skipped", append(vmLogAttrs(vm), "host_path", hostPath, "error", err.Error())...)
|
||||
}
|
||||
|
||||
func (d *Daemon) warnGitIdentitySyncSkipped(vm model.VMRecord, source string, err error) {
|
||||
if d.logger == nil || err == nil {
|
||||
func (s *WorkspaceService) warnGitIdentitySyncSkipped(vm model.VMRecord, source string, err error) {
|
||||
if s.logger == nil || err == nil {
|
||||
return
|
||||
}
|
||||
d.logger.Warn("guest git identity sync skipped", append(vmLogAttrs(vm), "source", source, "error", err.Error())...)
|
||||
s.logger.Warn("guest git identity sync skipped", append(vmLogAttrs(vm), "source", source, "error", err.Error())...)
|
||||
}
|
||||
|
||||
func mergeAuthorizedKey(existing, managed []byte) []byte {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue