daemon split (4/5): extract *VMService service
Phase 4 of the daemon god-struct refactor. VM lifecycle, create-op
registry, handle cache, disk provisioning, stats polling, ports
query, and the per-VM lock set all move off *Daemon onto *VMService.
Daemon keeps thin forwarders only for FindVM / TouchVM (dispatch
surface) and is otherwise out of VM lifecycle. Lazy-init via
d.vmSvc() mirrors the earlier services so test literals like
\`&Daemon{store: db, runner: r}\` still get a functional service
without spelling one out.
Three small cleanups along the way:
* preflight helpers (validateStartPrereqs / addBaseStartPrereqs
/ addBaseStartCommandPrereqs / validateWorkDiskResizePrereqs)
move with the VM methods that call them.
* cleanupRuntime / rebuildDNS move to *VMService, with
HostNetwork primitives (findFirecrackerPID, cleanupDMSnapshot,
killVMProcess, releaseTap, waitForExit, sendCtrlAltDel)
reached through s.net instead of the hostNet() facade.
* vsockAgentBinary becomes a package-level function so both
*Daemon (doctor) and *VMService (preflight) call one entry
point instead of each owning a forwarder method.
WorkspaceService's peer deps switch from eager method values to
closures — vmSvc() constructs VMService with WorkspaceService as a
peer, so resolving d.vmSvc().FindVM at construction time recursed
through workspaceSvc() → vmSvc(). Closures defer the lookup to call
time.
Pure code motion: build + unit tests green, lint clean. No RPC
surface or lock-ordering changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c0d456e734
commit
466a7c30c4
23 changed files with 655 additions and 463 deletions
|
|
@ -105,57 +105,57 @@ func removeHandlesFile(vmDir string) {
|
|||
// ensureHandleCache lazily constructs the cache so direct
|
||||
// `&Daemon{}` literals (common in tests) don't have to initialise
|
||||
// it. Production code goes through Open(), which also builds it.
|
||||
func (d *Daemon) ensureHandleCache() {
|
||||
if d.handles == nil {
|
||||
d.handles = newHandleCache()
|
||||
func (s *VMService) ensureHandleCache() {
|
||||
if s.handles == nil {
|
||||
s.handles = newHandleCache()
|
||||
}
|
||||
}
|
||||
|
||||
// setVMHandlesInMemory is a test-only cache seed that skips the
|
||||
// scratch-file write. Production callers should use setVMHandles so
|
||||
// the filesystem survives a daemon restart.
|
||||
func (d *Daemon) setVMHandlesInMemory(vmID string, h model.VMHandles) {
|
||||
if d == nil {
|
||||
func (s *VMService) setVMHandlesInMemory(vmID string, h model.VMHandles) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
d.ensureHandleCache()
|
||||
d.handles.set(vmID, h)
|
||||
s.ensureHandleCache()
|
||||
s.handles.set(vmID, h)
|
||||
}
|
||||
|
||||
// vmHandles returns the cached handles for vm (zero-value if no
|
||||
// entry). Call sites that previously read `vm.Runtime.{PID,...}`
|
||||
// should read through this instead.
|
||||
func (d *Daemon) vmHandles(vmID string) model.VMHandles {
|
||||
if d == nil {
|
||||
func (s *VMService) vmHandles(vmID string) model.VMHandles {
|
||||
if s == nil {
|
||||
return model.VMHandles{}
|
||||
}
|
||||
d.ensureHandleCache()
|
||||
h, _ := d.handles.get(vmID)
|
||||
s.ensureHandleCache()
|
||||
h, _ := s.handles.get(vmID)
|
||||
return h
|
||||
}
|
||||
|
||||
// setVMHandles updates the in-memory cache AND the per-VM scratch
|
||||
// file. Scratch-file errors are logged but not returned; the cache
|
||||
// write is authoritative while the daemon is alive.
|
||||
func (d *Daemon) setVMHandles(vm model.VMRecord, h model.VMHandles) {
|
||||
if d == nil {
|
||||
func (s *VMService) setVMHandles(vm model.VMRecord, h model.VMHandles) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
d.ensureHandleCache()
|
||||
d.handles.set(vm.ID, h)
|
||||
if err := writeHandlesFile(vm.Runtime.VMDir, h); err != nil && d.logger != nil {
|
||||
d.logger.Warn("persist handles.json failed", "vm_id", vm.ID, "error", err.Error())
|
||||
s.ensureHandleCache()
|
||||
s.handles.set(vm.ID, h)
|
||||
if err := writeHandlesFile(vm.Runtime.VMDir, h); err != nil && s.logger != nil {
|
||||
s.logger.Warn("persist handles.json failed", "vm_id", vm.ID, "error", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// clearVMHandles drops the cache entry and removes the scratch
|
||||
// file. Called on stop / delete / after a failed start.
|
||||
func (d *Daemon) clearVMHandles(vm model.VMRecord) {
|
||||
if d == nil {
|
||||
func (s *VMService) clearVMHandles(vm model.VMRecord) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
d.ensureHandleCache()
|
||||
d.handles.clear(vm.ID)
|
||||
s.ensureHandleCache()
|
||||
s.handles.clear(vm.ID)
|
||||
removeHandlesFile(vm.Runtime.VMDir)
|
||||
}
|
||||
|
||||
|
|
@ -164,11 +164,11 @@ func (d *Daemon) clearVMHandles(vm model.VMRecord) {
|
|||
// pattern, this reads the PID from the handle cache — which is
|
||||
// authoritative in-process — and verifies the PID against the api
|
||||
// socket so a recycled PID can't false-positive.
|
||||
func (d *Daemon) vmAlive(vm model.VMRecord) bool {
|
||||
func (s *VMService) vmAlive(vm model.VMRecord) bool {
|
||||
if vm.State != model.VMStateRunning {
|
||||
return false
|
||||
}
|
||||
h := d.vmHandles(vm.ID)
|
||||
h := s.vmHandles(vm.ID)
|
||||
if h.PID <= 0 {
|
||||
return false
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ func (d *Daemon) vmAlive(vm model.VMRecord) bool {
|
|||
// the daemon crashed but the PID changed on respawn — unlikely for
|
||||
// firecracker, but cheap insurance); fall back to verifying the
|
||||
// scratch file's PID directly.
|
||||
func (d *Daemon) rediscoverHandles(ctx context.Context, vm model.VMRecord) (model.VMHandles, bool, error) {
|
||||
func (s *VMService) rediscoverHandles(ctx context.Context, vm model.VMRecord) (model.VMHandles, bool, error) {
|
||||
saved, _, err := readHandlesFile(vm.Runtime.VMDir)
|
||||
if err != nil {
|
||||
return model.VMHandles{}, false, err
|
||||
|
|
@ -200,7 +200,7 @@ func (d *Daemon) rediscoverHandles(ctx context.Context, vm model.VMRecord) (mode
|
|||
if apiSock == "" {
|
||||
return saved, false, nil
|
||||
}
|
||||
if pid, pidErr := d.hostNet().findFirecrackerPID(ctx, apiSock); pidErr == nil && pid > 0 {
|
||||
if pid, pidErr := s.net.findFirecrackerPID(ctx, apiSock); pidErr == nil && pid > 0 {
|
||||
saved.PID = pid
|
||||
return saved, true, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue