Reconcile stale default image on startup
Upgrades from the pre-bundle layout can leave the unmanaged default image pointing at repo-root artifacts that no longer exist, causing vm start preflight to fail forever. Treat the default image as reconcilable state instead of bailing out when a record exists. Compute the desired default image from current runtime/config defaults, update an existing unmanaged default in place when its paths diverge, keep managed defaults untouched, and preserve the original ID so existing VMs keep referencing it. Added regression tests covering creation, no-op, reconciliation, managed-image skip, and missing-artifact behavior.
This commit is contained in:
parent
6e00fa690d
commit
ebb68c3126
2 changed files with 308 additions and 39 deletions
|
|
@ -3,6 +3,7 @@ package daemon
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -310,46 +311,90 @@ func (d *Daemon) ensureDefaultImage(ctx context.Context) error {
|
|||
if d.config.DefaultImageName == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := d.store.GetImageByName(ctx, d.config.DefaultImageName); err == nil {
|
||||
desired, ok := d.desiredDefaultImage()
|
||||
if !ok {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("default image already registered", "image_name", d.config.DefaultImageName)
|
||||
d.logger.Debug("default image skipped", "image_name", d.config.DefaultImageName, "rootfs_path", d.config.DefaultRootfs, "kernel_path", d.config.DefaultKernel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
rootfs := d.config.DefaultRootfs
|
||||
kernel := d.config.DefaultKernel
|
||||
initrd := d.config.DefaultInitrd
|
||||
if !exists(rootfs) || !exists(kernel) {
|
||||
|
||||
image, err := d.store.GetImageByName(ctx, d.config.DefaultImageName)
|
||||
switch {
|
||||
case err == nil:
|
||||
if image.Managed {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("managed default image left untouched", append(imageLogAttrs(image), "managed", image.Managed)...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if defaultImageMatches(image, desired) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("default image already current", imageLogAttrs(image)...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
updated := desired
|
||||
updated.ID = image.ID
|
||||
updated.CreatedAt = image.CreatedAt
|
||||
updated.UpdatedAt = model.Now()
|
||||
if err := d.store.UpsertImage(ctx, updated); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("default image skipped", "image_name", d.config.DefaultImageName, "rootfs_path", rootfs, "kernel_path", kernel)
|
||||
d.logger.Info("default image reconciled", append(imageLogAttrs(updated), "previous_rootfs_path", image.RootfsPath, "previous_kernel_path", image.KernelPath)...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
id, err := model.NewID()
|
||||
if err != nil {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
id, err := model.NewID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := model.Now()
|
||||
desired.ID = id
|
||||
desired.CreatedAt = now
|
||||
desired.UpdatedAt = now
|
||||
if err := d.store.UpsertImage(ctx, desired); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.logger != nil {
|
||||
d.logger.Info("default image registered", append(imageLogAttrs(desired), "managed", desired.Managed)...)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
now := model.Now()
|
||||
image := model.Image{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
func (d *Daemon) desiredDefaultImage() (model.Image, bool) {
|
||||
rootfs := d.config.DefaultRootfs
|
||||
kernel := d.config.DefaultKernel
|
||||
if !exists(rootfs) || !exists(kernel) {
|
||||
return model.Image{}, false
|
||||
}
|
||||
return model.Image{
|
||||
Name: d.config.DefaultImageName,
|
||||
Managed: false,
|
||||
ArtifactDir: "",
|
||||
RootfsPath: rootfs,
|
||||
KernelPath: kernel,
|
||||
InitrdPath: initrd,
|
||||
InitrdPath: d.config.DefaultInitrd,
|
||||
ModulesDir: d.config.DefaultModulesDir,
|
||||
PackagesPath: d.config.DefaultPackagesFile,
|
||||
Docker: strings.Contains(filepath.Base(rootfs), "docker"),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := d.store.UpsertImage(ctx, image); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.logger != nil {
|
||||
d.logger.Info("default image registered", append(imageLogAttrs(image), "managed", image.Managed)...)
|
||||
}
|
||||
return nil
|
||||
}, true
|
||||
}
|
||||
|
||||
func defaultImageMatches(current, desired model.Image) bool {
|
||||
return current.Name == desired.Name &&
|
||||
current.Managed == desired.Managed &&
|
||||
current.ArtifactDir == desired.ArtifactDir &&
|
||||
current.RootfsPath == desired.RootfsPath &&
|
||||
current.KernelPath == desired.KernelPath &&
|
||||
current.InitrdPath == desired.InitrdPath &&
|
||||
current.ModulesDir == desired.ModulesDir &&
|
||||
current.PackagesPath == desired.PackagesPath &&
|
||||
current.Docker == desired.Docker
|
||||
}
|
||||
|
||||
func (d *Daemon) reconcile(ctx context.Context) error {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue