daemon split (2/5): extract *ImageService service
Second phase of splitting the daemon god-struct. ImageService now owns
all image + kernel registry operations: register/promote/delete/pull
for images (bundle + OCI paths), the six kernel commands, and the
shared SSH-key/work-seed injection helpers. imageOpsMu (the
publication-window lock) lives on the service; so do the three OCI
pull test seams pullAndFlatten / finalizePulledRootfs / bundleFetch.
The four files images.go, images_pull.go, image_seed.go, kernels.go
flipped their receivers from *Daemon to *ImageService.
FindImage moved with the service. Daemon keeps a thin FindImage
forwarder so callers reading the dispatch code see the obvious
facade and tests that pre-date the split still compile.
flattenNestedWorkHome — called from image_seed.go, vm_authsync.go,
and vm_disk.go across future service boundaries — became a
package-level helper taking a CommandRunner explicitly. Daemon keeps
a deprecated forwarder for now; the other services will use the
package form.
Lazy-init helper imageSvc() on Daemon mirrors hostNet() from
Phase 1, so test literals like &Daemon{store: db, runner: r, ...}
that don't spell out an ImageService still get a working one.
Tests that override the image test seams (autopull_test,
concurrency_test, images_pull_test, images_pull_bundle_test) now
assign d.img = &ImageService{...seams...}; the two-statement pattern
matches what Phase 1 established for HostNetwork.
Dispatch in daemon.go is cleaner now: every image/kernel RPC handler
is a single-liner forwarding to d.imageSvc().*. Phase 5 will do the
same for VM lifecycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
362009d747
commit
d7614a3b2b
15 changed files with 389 additions and 209 deletions
|
|
@ -19,8 +19,6 @@ import (
|
|||
"banger/internal/config"
|
||||
"banger/internal/daemon/opstate"
|
||||
ws "banger/internal/daemon/workspace"
|
||||
"banger/internal/imagecat"
|
||||
"banger/internal/imagepull"
|
||||
"banger/internal/model"
|
||||
"banger/internal/paths"
|
||||
"banger/internal/rpc"
|
||||
|
|
@ -35,7 +33,6 @@ type Daemon struct {
|
|||
store *store.Store
|
||||
runner system.CommandRunner
|
||||
logger *slog.Logger
|
||||
imageOpsMu sync.Mutex
|
||||
createVMMu sync.Mutex
|
||||
createOps opstate.Registry[*vmCreateOperationState]
|
||||
vmLocks vmLockSet
|
||||
|
|
@ -53,14 +50,12 @@ type Daemon struct {
|
|||
// handles.json scratch file and OS inspection.
|
||||
handles *handleCache
|
||||
net *HostNetwork
|
||||
img *ImageService
|
||||
closing chan struct{}
|
||||
once sync.Once
|
||||
pid int
|
||||
listener net.Listener
|
||||
vmCaps []vmCapability
|
||||
pullAndFlatten func(ctx context.Context, ref, cacheDir, destDir string) (imagepull.Metadata, error)
|
||||
finalizePulledRootfs func(ctx context.Context, ext4File string, meta imagepull.Metadata) error
|
||||
bundleFetch func(ctx context.Context, destDir string, entry imagecat.CatEntry) (imagecat.Manifest, error)
|
||||
requestHandler func(context.Context, rpc.Request) rpc.Response
|
||||
guestWaitForSSH func(context.Context, string, string, time.Duration) error
|
||||
guestDial func(context.Context, string, string) (guestSSHClient, error)
|
||||
|
|
@ -449,68 +444,68 @@ func (d *Daemon) dispatch(ctx context.Context, req rpc.Request) rpc.Response {
|
|||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
image, err := d.FindImage(ctx, params.IDOrName)
|
||||
image, err := d.imageSvc().FindImage(ctx, params.IDOrName)
|
||||
return marshalResultOrError(api.ImageShowResult{Image: image}, err)
|
||||
case "image.register":
|
||||
params, err := rpc.DecodeParams[api.ImageRegisterParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
image, err := d.RegisterImage(ctx, params)
|
||||
image, err := d.imageSvc().RegisterImage(ctx, params)
|
||||
return marshalResultOrError(api.ImageShowResult{Image: image}, err)
|
||||
case "image.promote":
|
||||
params, err := rpc.DecodeParams[api.ImageRefParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
image, err := d.PromoteImage(ctx, params.IDOrName)
|
||||
image, err := d.imageSvc().PromoteImage(ctx, params.IDOrName)
|
||||
return marshalResultOrError(api.ImageShowResult{Image: image}, err)
|
||||
case "image.delete":
|
||||
params, err := rpc.DecodeParams[api.ImageRefParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
image, err := d.DeleteImage(ctx, params.IDOrName)
|
||||
image, err := d.imageSvc().DeleteImage(ctx, params.IDOrName)
|
||||
return marshalResultOrError(api.ImageShowResult{Image: image}, err)
|
||||
case "image.pull":
|
||||
params, err := rpc.DecodeParams[api.ImagePullParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
image, err := d.PullImage(ctx, params)
|
||||
image, err := d.imageSvc().PullImage(ctx, params)
|
||||
return marshalResultOrError(api.ImageShowResult{Image: image}, err)
|
||||
case "kernel.list":
|
||||
return marshalResultOrError(d.KernelList(ctx))
|
||||
return marshalResultOrError(d.imageSvc().KernelList(ctx))
|
||||
case "kernel.show":
|
||||
params, err := rpc.DecodeParams[api.KernelRefParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
entry, err := d.KernelShow(ctx, params.Name)
|
||||
entry, err := d.imageSvc().KernelShow(ctx, params.Name)
|
||||
return marshalResultOrError(api.KernelShowResult{Entry: entry}, err)
|
||||
case "kernel.delete":
|
||||
params, err := rpc.DecodeParams[api.KernelRefParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
err = d.KernelDelete(ctx, params.Name)
|
||||
err = d.imageSvc().KernelDelete(ctx, params.Name)
|
||||
return marshalResultOrError(api.Empty{}, err)
|
||||
case "kernel.import":
|
||||
params, err := rpc.DecodeParams[api.KernelImportParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
entry, err := d.KernelImport(ctx, params)
|
||||
entry, err := d.imageSvc().KernelImport(ctx, params)
|
||||
return marshalResultOrError(api.KernelShowResult{Entry: entry}, err)
|
||||
case "kernel.pull":
|
||||
params, err := rpc.DecodeParams[api.KernelPullParams](req)
|
||||
if err != nil {
|
||||
return rpc.NewError("bad_request", err.Error())
|
||||
}
|
||||
entry, err := d.KernelPull(ctx, params)
|
||||
entry, err := d.imageSvc().KernelPull(ctx, params)
|
||||
return marshalResultOrError(api.KernelShowResult{Entry: entry}, err)
|
||||
case "kernel.catalog":
|
||||
return marshalResultOrError(d.KernelCatalog(ctx))
|
||||
return marshalResultOrError(d.imageSvc().KernelCatalog(ctx))
|
||||
default:
|
||||
return rpc.NewError("unknown_method", req.Method)
|
||||
}
|
||||
|
|
@ -619,35 +614,11 @@ func (d *Daemon) FindVM(ctx context.Context, idOrName string) (model.VMRecord, e
|
|||
return model.VMRecord{}, fmt.Errorf("vm %q not found", idOrName)
|
||||
}
|
||||
|
||||
// FindImage stays on Daemon as a thin forwarder to the image service
|
||||
// lookup so callers reading dispatch code see the obvious facade, and
|
||||
// tests that pre-date the service split still compile.
|
||||
func (d *Daemon) FindImage(ctx context.Context, idOrName string) (model.Image, error) {
|
||||
if idOrName == "" {
|
||||
return model.Image{}, errors.New("image id or name is required")
|
||||
}
|
||||
if image, err := d.store.GetImageByName(ctx, idOrName); err == nil {
|
||||
return image, nil
|
||||
}
|
||||
if image, err := d.store.GetImageByID(ctx, idOrName); err == nil {
|
||||
return image, nil
|
||||
}
|
||||
images, err := d.store.ListImages(ctx)
|
||||
if err != nil {
|
||||
return model.Image{}, err
|
||||
}
|
||||
matchCount := 0
|
||||
var match model.Image
|
||||
for _, image := range images {
|
||||
if strings.HasPrefix(image.ID, idOrName) || strings.HasPrefix(image.Name, idOrName) {
|
||||
match = image
|
||||
matchCount++
|
||||
}
|
||||
}
|
||||
if matchCount == 1 {
|
||||
return match, nil
|
||||
}
|
||||
if matchCount > 1 {
|
||||
return model.Image{}, fmt.Errorf("multiple images match %q", idOrName)
|
||||
}
|
||||
return model.Image{}, fmt.Errorf("image %q not found", idOrName)
|
||||
return d.imageSvc().FindImage(ctx, idOrName)
|
||||
}
|
||||
|
||||
func (d *Daemon) TouchVM(ctx context.Context, idOrName string) (model.VMRecord, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue