Beat VM create wall time without changing VM semantics. Generate a work-seed ext4 sidecar during image builds and rootfs rebuilds, then clone and resize that seed for each new VM instead of rebuilding /root from scratch. Plumb the new seed artifact through config, runtime metadata, store state, runtime-bundle defaults, doctor checks, and default-image reconciliation so older images still fall back cleanly. Add a daemon TAP pool to keep idle bridge-attached devices warm, expose stage timing in lifecycle logs, add a create/SSH benchmark script plus Make target, and teach verify.sh that tap-pool-* devices are reusable capacity rather than cleanup leaks. Validated with go test ./..., make build, ./verify.sh, and make bench-create ARGS="--runs 2".
155 lines
3.4 KiB
Go
155 lines
3.4 KiB
Go
package system
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
const (
|
|
minWorkSeedBytes int64 = 512 * 1024 * 1024
|
|
workSeedSlackBytes int64 = 256 * 1024 * 1024
|
|
workSeedRoundBytes int64 = 64 * 1024 * 1024
|
|
)
|
|
|
|
func CopyFilePreferClone(sourcePath, targetPath string) error {
|
|
source, err := os.Open(sourcePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer source.Close()
|
|
|
|
info, err := source.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
target, err := os.OpenFile(targetPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode().Perm())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer target.Close()
|
|
|
|
if err := unix.IoctlFileClone(int(target.Fd()), int(source.Fd())); err == nil {
|
|
return nil
|
|
}
|
|
if _, err := source.Seek(0, io.SeekStart); err != nil {
|
|
return err
|
|
}
|
|
if _, err := target.Seek(0, io.SeekStart); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.Copy(target, source); err != nil {
|
|
return err
|
|
}
|
|
if err := target.Sync(); err != nil {
|
|
return err
|
|
}
|
|
if err := target.Chmod(info.Mode().Perm()); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func WorkSeedPath(rootfsPath string) string {
|
|
rootfsPath = strings.TrimSpace(rootfsPath)
|
|
if rootfsPath == "" {
|
|
return ""
|
|
}
|
|
if strings.HasSuffix(rootfsPath, ".ext4") {
|
|
return strings.TrimSuffix(rootfsPath, ".ext4") + ".work-seed.ext4"
|
|
}
|
|
return rootfsPath + ".work-seed"
|
|
}
|
|
|
|
func BuildWorkSeedImage(ctx context.Context, runner CommandRunner, rootfsPath, outPath string) error {
|
|
rootMount, cleanupRoot, err := MountTempDir(ctx, runner, rootfsPath, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cleanupRoot()
|
|
|
|
rootHome := filepath.Join(rootMount, "root")
|
|
sizeBytes, err := estimateWorkSeedSize(rootHome)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.RemoveAll(outPath); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
file, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := file.Close(); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Truncate(outPath, sizeBytes); err != nil {
|
|
return err
|
|
}
|
|
if _, err := runner.Run(ctx, "mkfs.ext4", "-F", outPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
workMount, cleanupWork, err := MountTempDir(ctx, runner, outPath, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cleanupWork()
|
|
|
|
return CopyDirContents(ctx, runner, rootHome, workMount, true)
|
|
}
|
|
|
|
func estimateWorkSeedSize(rootHome string) (int64, error) {
|
|
var usedBytes int64
|
|
err := filepath.Walk(rootHome, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.Mode().IsRegular() {
|
|
usedBytes += info.Size()
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
sizeBytes := usedBytes*2 + workSeedSlackBytes
|
|
if sizeBytes < minWorkSeedBytes {
|
|
sizeBytes = minWorkSeedBytes
|
|
}
|
|
if rem := sizeBytes % workSeedRoundBytes; rem != 0 {
|
|
sizeBytes += workSeedRoundBytes - rem
|
|
}
|
|
return sizeBytes, nil
|
|
}
|
|
|
|
func ReadNormalizedLines(path string) ([]string, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []string
|
|
for _, line := range strings.Split(string(data), "\n") {
|
|
if strings.HasSuffix(line, "\r") {
|
|
line = strings.TrimSuffix(line, "\r")
|
|
}
|
|
if idx := strings.Index(line, "#"); idx >= 0 {
|
|
line = line[:idx]
|
|
}
|
|
line = strings.TrimSpace(line)
|
|
if line == "" {
|
|
continue
|
|
}
|
|
out = append(out, line)
|
|
}
|
|
if len(out) == 0 {
|
|
return nil, fmt.Errorf("file has no entries: %s", path)
|
|
}
|
|
return out, nil
|
|
}
|