feat: robust provisioning (DNS self-heal) + live execution feedback in chat
Production session provisioned the container but the service never installed:
apt failed with "Temporary failure resolving deb.debian.org" — a static-IP LXC
whose assigned nameserver couldn't resolve. The operator also got zero feedback:
the approval banner just sat there with no running/complete/failed status.
Backend robustness (provisionScript):
- Wait for real DNS/connectivity inside the container before apt, and self-heal
/etc/resolv.conf to a public resolver (1.1.1.1/8.8.8.8) if the assigned one
is dead. `set -e` after the gate so apt/post_install failures surface.
- apt-get update/install with Acquire::Retries=3.
Frontend feedback (InlineApproval):
- After approve, poll GET /executions/{id} and show live phase: submitting →
provisioning… → provisioned successfully / execution failed (with the error).
- add getExecution() to api.ts.
Agent guidance (SOUL.md):
- omit vmid (auto-assigned), prefer dhcp, docker-compose-plugin is not in Debian
(use docker.io + get.docker.com), end post_install with a health check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package httpapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -87,6 +88,34 @@ func TestJSONErrValidForNastyOutput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionScript(t *testing.T) {
|
||||
s := provisionScript([]string{"docker.io", "git"}, "echo hi > /root/x")
|
||||
// Network/DNS gate must come before apt.
|
||||
gate := strings.Index(s, "getent hosts")
|
||||
apt := strings.Index(s, "apt-get update")
|
||||
post := strings.Index(s, "echo hi > /root/x")
|
||||
if gate < 0 || apt < 0 || post < 0 {
|
||||
t.Fatalf("missing sections: gate=%d apt=%d post=%d\n%s", gate, apt, post, s)
|
||||
}
|
||||
if !(gate < apt && apt < post) {
|
||||
t.Errorf("wrong ordering: gate=%d apt=%d post=%d", gate, apt, post)
|
||||
}
|
||||
if !strings.Contains(s, "nameserver 1.1.1.1") {
|
||||
t.Error("missing DNS self-heal fallback")
|
||||
}
|
||||
if !strings.Contains(s, "docker.io git") {
|
||||
t.Error("packages not joined into install line")
|
||||
}
|
||||
// No packages: no apt lines, but post_install and gate still present.
|
||||
s2 := provisionScript(nil, "systemctl status foo")
|
||||
if strings.Contains(s2, "apt-get install") {
|
||||
t.Error("apt install should be absent when no packages requested")
|
||||
}
|
||||
if !strings.Contains(s2, "systemctl status foo") || !strings.Contains(s2, "getent hosts") {
|
||||
t.Error("post_install or gate missing in no-package case")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizePkgs(t *testing.T) {
|
||||
in := []string{"docker.io", "git", "rm -rf /", "curl;wget", "python3-pip", ""}
|
||||
got := sanitizePkgs(in)
|
||||
|
||||
Reference in New Issue
Block a user