Manage image artifacts and show VM create progress
Stop relying on ad hoc rootfs handling by adding image promotion, managed work-seed fingerprint metadata, and lazy self-healing for older managed images after the first create. Rebuild guest images with baked SSH access, a guest NIC bootstrap, and default opencode services, and add the staged Void kernel/initramfs/modules workflow so void-exp uses a matching Void boot stack. Replace the opaque blocking vm.create RPC with a begin/status flow that prints live stages in the CLI while still waiting for vsock health and opencode on guest port 4096. Validate with GOCACHE=/tmp/banger-gocache go test ./... and live void-exp create/delete smoke runs.
This commit is contained in:
parent
9f09b0d25c
commit
30f0c0b54a
37 changed files with 2334 additions and 99 deletions
86
internal/daemon/image_seed.go
Normal file
86
internal/daemon/image_seed.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"banger/internal/guest"
|
||||
"banger/internal/model"
|
||||
"banger/internal/system"
|
||||
)
|
||||
|
||||
func (d *Daemon) seedAuthorizedKeyOnExt4Image(ctx context.Context, imagePath string) (string, error) {
|
||||
if strings.TrimSpace(d.config.SSHKeyPath) == "" {
|
||||
return "", nil
|
||||
}
|
||||
fingerprint, err := guest.AuthorizedPublicKeyFingerprint(d.config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("derive authorized ssh key fingerprint: %w", err)
|
||||
}
|
||||
publicKey, err := guest.AuthorizedPublicKey(d.config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("derive authorized ssh key: %w", err)
|
||||
}
|
||||
mountDir, cleanup, err := system.MountTempDir(ctx, d.runner, imagePath, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
if err := d.flattenNestedWorkHome(ctx, mountDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sshDir := filepath.Join(mountDir, ".ssh")
|
||||
if _, err := d.runner.RunSudo(ctx, "mkdir", "-p", sshDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := d.runner.RunSudo(ctx, "chmod", "700", sshDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
authorizedKeysPath := filepath.Join(sshDir, "authorized_keys")
|
||||
existing, err := d.runner.RunSudo(ctx, "cat", authorizedKeysPath)
|
||||
if err != nil {
|
||||
existing = nil
|
||||
}
|
||||
merged := mergeAuthorizedKey(existing, publicKey)
|
||||
tmpFile, err := os.CreateTemp("", "banger-image-authorized-keys-*")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
if _, err := tmpFile.Write(merged); err != nil {
|
||||
_ = tmpFile.Close()
|
||||
_ = os.Remove(tmpPath)
|
||||
return "", err
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
_ = os.Remove(tmpPath)
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(tmpPath)
|
||||
if _, err := d.runner.RunSudo(ctx, "install", "-m", "600", tmpPath, authorizedKeysPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fingerprint, nil
|
||||
}
|
||||
|
||||
func (d *Daemon) refreshManagedWorkSeedFingerprint(ctx context.Context, image model.Image, fingerprint string) error {
|
||||
if !image.Managed || strings.TrimSpace(image.WorkSeedPath) == "" || strings.TrimSpace(fingerprint) == "" {
|
||||
return nil
|
||||
}
|
||||
seededFingerprint, err := d.seedAuthorizedKeyOnExt4Image(ctx, image.WorkSeedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if seededFingerprint == "" || seededFingerprint == image.SeededSSHPublicKeyFingerprint {
|
||||
return nil
|
||||
}
|
||||
image.SeededSSHPublicKeyFingerprint = seededFingerprint
|
||||
image.UpdatedAt = model.Now()
|
||||
return d.store.UpsertImage(ctx, image)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue