feat: show golden VM tag in clone list, add console logging, fix ubuntu boot
- Persist golden VM tag to clones/{id}/tag at spawn time
- GET /clones now returns [{id, tag}] objects instead of plain IDs
- Web UI renders tag as a dim label next to each clone entry (clone 3 · default)
- Pre-existing fixes included in this commit:
- console: tee all PTY output to clones/{id}/console.log for boot capture
- network: destroy stale tap before recreating to avoid EBUSY errors
- orchestrator: fix ubuntu systemd boot (custom fc-console.service, fstab,
mask serial-getty udev dep, longer settle time, correct package list)
- config: remove quiet/loglevel=0 from default boot args
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,17 @@ func RunConsoleProxy(cfg Config, id int, tapName, tag string) error {
|
||||
logger.Infof("clone %d: restored in %s (pid=%d, tap=%s)",
|
||||
id, elapsed.Round(time.Millisecond), cmd.Process.Pid, tapName)
|
||||
|
||||
// --- Open console log (captures all serial output from boot) ---
|
||||
consoleLogPath := filepath.Join(cloneDir, "console.log")
|
||||
consoleLog, err := os.OpenFile(consoleLogPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
logger.Warnf("could not open console log: %v", err)
|
||||
consoleLog = nil
|
||||
}
|
||||
if consoleLog != nil {
|
||||
defer consoleLog.Close()
|
||||
}
|
||||
|
||||
// --- Create console socket ---
|
||||
os.Remove(consoleSockPath) //nolint:errcheck
|
||||
listener, err := net.Listen("unix", consoleSockPath)
|
||||
@@ -191,7 +202,7 @@ func RunConsoleProxy(cfg Config, id int, tapName, tag string) error {
|
||||
if resizeListener != nil {
|
||||
go serveResize(resizeListener, ptm, vmDone, logger)
|
||||
}
|
||||
serveConsole(listener, ptm, vmDone, logger)
|
||||
serveConsole(listener, ptm, consoleLog, vmDone, logger)
|
||||
|
||||
listener.Close()
|
||||
if resizeListener != nil {
|
||||
@@ -274,16 +285,20 @@ func (a *atomicWriter) Write(p []byte) (int, error) {
|
||||
// A background goroutine reads from the PTY master continuously (discarding
|
||||
// output when no client is connected so the VM never blocks on a full buffer).
|
||||
// Only one client is served at a time; sessions are serialised.
|
||||
func serveConsole(listener net.Listener, ptm *os.File, vmDone <-chan struct{}, logger *log.Entry) {
|
||||
func serveConsole(listener net.Listener, ptm *os.File, logFile *os.File, vmDone <-chan struct{}, logger *log.Entry) {
|
||||
aw := &atomicWriter{w: io.Discard}
|
||||
|
||||
// Background PTY reader — runs for the full VM lifetime.
|
||||
// All output is tee'd to logFile (if set) so boot messages are never lost.
|
||||
go func() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
n, err := ptm.Read(buf)
|
||||
if n > 0 {
|
||||
aw.Write(buf[:n]) //nolint:errcheck
|
||||
if logFile != nil {
|
||||
logFile.Write(buf[:n]) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return // PTY closed (VM exited)
|
||||
|
||||
Reference in New Issue
Block a user