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
|
|
@ -14,8 +14,10 @@ import (
|
|||
|
||||
"banger/internal/firecracker"
|
||||
"banger/internal/guest"
|
||||
"banger/internal/guestnet"
|
||||
"banger/internal/hostnat"
|
||||
"banger/internal/model"
|
||||
"banger/internal/opencode"
|
||||
"banger/internal/system"
|
||||
"banger/internal/vsockagent"
|
||||
)
|
||||
|
|
@ -103,6 +105,10 @@ func (d *Daemon) runImageBuildNative(ctx context.Context, spec imageBuildSpec) (
|
|||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
authorizedKey, err := guest.AuthorizedPublicKey(d.config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
helperBytes, err := os.ReadFile(d.config.VSockAgentPath)
|
||||
if err != nil {
|
||||
|
|
@ -117,7 +123,7 @@ func (d *Daemon) runImageBuildNative(ctx context.Context, spec imageBuildSpec) (
|
|||
if err := writeBuildLog(spec.BuildLog, "configuring guest"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.RunScript(ctx, buildProvisionScript(vm.Name, d.config.DefaultDNS, packages, spec.InstallDocker), spec.BuildLog); err != nil {
|
||||
if err := client.RunScript(ctx, buildProvisionScript(vm.Name, d.config.DefaultDNS, string(authorizedKey), packages, spec.InstallDocker), spec.BuildLog); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(spec.ModulesDir) != "" {
|
||||
|
|
@ -250,7 +256,7 @@ func (d *Daemon) shutdownImageBuildVM(ctx context.Context, vm imageBuildVM) erro
|
|||
return d.waitForExit(ctx, vm.PID, vm.APISock, 15*time.Second)
|
||||
}
|
||||
|
||||
func buildProvisionScript(vmName, dnsServer string, packages []string, installDocker bool) string {
|
||||
func buildProvisionScript(vmName, dnsServer, authorizedKey string, packages []string, installDocker bool) string {
|
||||
var script bytes.Buffer
|
||||
script.WriteString("set -euo pipefail\n")
|
||||
fmt.Fprintf(&script, "printf 'nameserver %%s\\n' %s > /etc/resolv.conf\n", shellQuote(dnsServer))
|
||||
|
|
@ -260,11 +266,14 @@ func buildProvisionScript(vmName, dnsServer string, packages []string, installDo
|
|||
script.WriteString("sed -i '\\|^/dev/vdb[[:space:]]\\+/home[[:space:]]|d; \\|^/dev/vdc[[:space:]]\\+/var[[:space:]]|d' /etc/fstab\n")
|
||||
script.WriteString("if ! grep -q '^tmpfs /run ' /etc/fstab; then echo 'tmpfs /run tmpfs defaults,nodev,nosuid,mode=0755 0 0' >> /etc/fstab; fi\n")
|
||||
script.WriteString("if ! grep -q '^tmpfs /tmp ' /etc/fstab; then echo 'tmpfs /tmp tmpfs defaults,nodev,nosuid,mode=1777 0 0' >> /etc/fstab; fi\n")
|
||||
appendAuthorizedKeySetup(&script, authorizedKey)
|
||||
script.WriteString("apt-get update\n")
|
||||
script.WriteString("DEBIAN_FRONTEND=noninteractive apt-get -y upgrade\n")
|
||||
fmt.Fprintf(&script, "PACKAGES=%s\n", shellArray(packages))
|
||||
script.WriteString("DEBIAN_FRONTEND=noninteractive apt-get -y install \"${PACKAGES[@]}\"\n")
|
||||
appendGuestNetworkSetup(&script)
|
||||
appendMiseSetup(&script)
|
||||
appendOpenCodeServiceSetup(&script)
|
||||
appendTmuxSetup(&script)
|
||||
appendVSockPingSetup(&script)
|
||||
if installDocker {
|
||||
|
|
@ -279,6 +288,15 @@ func buildProvisionScript(vmName, dnsServer string, packages []string, installDo
|
|||
return script.String()
|
||||
}
|
||||
|
||||
func appendAuthorizedKeySetup(script *bytes.Buffer, authorizedKey string) {
|
||||
script.WriteString("mkdir -p /root/.ssh\n")
|
||||
script.WriteString("chmod 700 /root/.ssh\n")
|
||||
script.WriteString("cat > /root/.ssh/authorized_keys <<'EOF'\n")
|
||||
script.WriteString(strings.TrimSpace(authorizedKey))
|
||||
script.WriteString("\nEOF\n")
|
||||
script.WriteString("chmod 600 /root/.ssh/authorized_keys\n")
|
||||
}
|
||||
|
||||
func buildModulesCommand(modulesBase string) string {
|
||||
return fmt.Sprintf("bash -se <<'EOF'\nset -euo pipefail\nmkdir -p /lib/modules\ntar -C /lib/modules -xf -\ndepmod -a %s\nmkdir -p /etc/modules-load.d\nprintf 'nf_tables\\nnft_chain_nat\\nveth\\nbr_netfilter\\noverlay\\n' > /etc/modules-load.d/docker-netfilter.conf\nmkdir -p /etc/sysctl.d\ncat > /etc/sysctl.d/99-docker.conf <<'SYSCTL'\nnet.bridge.bridge-nf-call-iptables = 1\nnet.bridge.bridge-nf-call-ip6tables = 1\nnet.ipv4.ip_forward = 1\nSYSCTL\nsysctl --system >/dev/null 2>&1 || true\nEOF", shellQuote(modulesBase))
|
||||
}
|
||||
|
|
@ -286,6 +304,9 @@ func buildModulesCommand(modulesBase string) string {
|
|||
func appendMiseSetup(script *bytes.Buffer) {
|
||||
fmt.Fprintf(script, "curl -fsSL https://mise.run | MISE_INSTALL_PATH=%s MISE_VERSION=%s sh\n", shellQuote(defaultMiseInstallPath), shellQuote(defaultMiseVersion))
|
||||
fmt.Fprintf(script, "%s use -g %s\n", shellQuote(defaultMiseInstallPath), shellQuote(defaultOpenCodeTool))
|
||||
fmt.Fprintf(script, "%s reshim\n", shellQuote(defaultMiseInstallPath))
|
||||
fmt.Fprintf(script, "if [[ ! -e %s ]]; then echo 'opencode shim not found after mise install' >&2; exit 1; fi\n", shellQuote(opencode.ShimPath))
|
||||
fmt.Fprintf(script, "ln -snf %s %s\n", shellQuote(opencode.ShimPath), shellQuote(opencode.GuestBinaryPath))
|
||||
script.WriteString("mkdir -p /etc/profile.d\n")
|
||||
script.WriteString("cat > /etc/profile.d/mise.sh <<'EOF'\n")
|
||||
fmt.Fprintf(script, "if [ -n \"${BASH_VERSION:-}\" ] && [ -x %s ]; then\n", shellQuote(defaultMiseInstallPath))
|
||||
|
|
@ -296,6 +317,28 @@ func appendMiseSetup(script *bytes.Buffer) {
|
|||
appendLineIfMissing(script, "/etc/bash.bashrc", defaultMiseActivateLine)
|
||||
}
|
||||
|
||||
func appendGuestNetworkSetup(script *bytes.Buffer) {
|
||||
script.WriteString("mkdir -p /usr/local/libexec /etc/systemd/system\n")
|
||||
script.WriteString("cat > " + guestnet.GuestScriptPath + " <<'EOF'\n")
|
||||
script.WriteString(guestnet.BootstrapScript())
|
||||
script.WriteString("EOF\n")
|
||||
script.WriteString("chmod 0755 " + guestnet.GuestScriptPath + "\n")
|
||||
script.WriteString("cat > /etc/systemd/system/" + guestnet.SystemdServiceName + " <<'EOF'\n")
|
||||
script.WriteString(guestnet.SystemdServiceUnit())
|
||||
script.WriteString("EOF\n")
|
||||
script.WriteString("chmod 0644 /etc/systemd/system/" + guestnet.SystemdServiceName + "\n")
|
||||
script.WriteString("if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload || true; systemctl enable --now " + guestnet.SystemdServiceName + " || true; fi\n")
|
||||
}
|
||||
|
||||
func appendOpenCodeServiceSetup(script *bytes.Buffer) {
|
||||
script.WriteString("mkdir -p /etc/systemd/system\n")
|
||||
script.WriteString("cat > /etc/systemd/system/" + opencode.ServiceName + " <<'EOF'\n")
|
||||
script.WriteString(opencode.ServiceUnit())
|
||||
script.WriteString("EOF\n")
|
||||
script.WriteString("chmod 0644 /etc/systemd/system/" + opencode.ServiceName + "\n")
|
||||
script.WriteString("if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload || true; systemctl enable --now " + opencode.ServiceName + " || true; fi\n")
|
||||
}
|
||||
|
||||
func appendTmuxSetup(script *bytes.Buffer) {
|
||||
fmt.Fprintf(script, "TMUX_PLUGIN_DIR=%s\n", shellQuote(defaultTMUXPluginDir))
|
||||
fmt.Fprintf(script, "TMUX_RESURRECT_DIR=%s\n", shellQuote(defaultTMUXResurrectDir))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue