firewall.enabled (default true) is a cluster setting, applied live: the leader sends a Firewall message to every agent when it registers and when the setting or firewall_networks changes, and the agent (root) takes llmgw's nft table out or installs it. A control plane writes the state to /var/lib/llmgw/firewall.json, which llmgw-firewall-sync.path applies as root with `llmgw firewall sync`; server init and server add install the units, `llmgw firewall sync --install` on one set up before. node add and server add put no firewall on a machine while the setting is off. `llmgw wipe` removes llmgw from a machine after listing what goes and asking for its name: units, engines' processes, the nft table, /etc/llmgw, /var/lib/llmgw, the agent's state, the sudo grant, the CA from the trust store, keepalived's configuration when llmgw wrote it, and the users. --models, --store (Patroni member, etcd, or the local database) and --all take more. The console has it under Do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
107 lines
3.9 KiB
Go
107 lines
3.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func wipeFixture(t *testing.T) string {
|
|
t.Helper()
|
|
root := t.TempDir()
|
|
for p, body := range map[string]string{
|
|
"/etc/llmgw/server.yaml": "listen: :8080\n",
|
|
"/etc/llmgw/pki/ca-key.pem": "key",
|
|
"/var/lib/llmgw/admin-token": "t",
|
|
"/var/lib/llmgw-agent/control-planes.json": "[]",
|
|
"/etc/systemd/system/llmgw-server.service": "[Unit]",
|
|
"/etc/systemd/system/llmgw-agent.service": "[Unit]",
|
|
"/etc/sudoers.d/llmgw-setup": "x",
|
|
"/etc/keepalived/keepalived.conf": "# llmgw: the gateway's floating address\n",
|
|
"/etc/passwd": "root:x:0:0::/root:/bin/sh\nllmgw:x:999:999::/var/lib/llmgw:/usr/sbin/nologin\nllmgw-engine:x:998:998::/:/usr/sbin/nologin\n",
|
|
"/srv/models/qwen/v1/q.gguf": "w",
|
|
"/opt/llmgw/engines/llamacpp/b1/llama-server": "e",
|
|
"/etc/patroni/config.yml": "scope: llmgw\n",
|
|
"/var/lib/postgresql/16/llmgw/PG_VERSION": "16",
|
|
"/etc/default/etcd": "ETCD_INITIAL_CLUSTER_TOKEN=llmgw\n",
|
|
"/var/lib/etcd/default/member/snap": "s",
|
|
"/usr/local/bin/llmgw": "bin",
|
|
} {
|
|
full := filepath.Join(root, p)
|
|
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(full, []byte(body), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return root
|
|
}
|
|
|
|
func TestWipeRemovesTheConfigurationAndKeepsWhatWasNotAsked(t *testing.T) {
|
|
root := wipeFixture(t)
|
|
var ran []string
|
|
w := &wiper{root: root, run: func(name string, args ...string) error {
|
|
ran = append(ran, name+" "+strings.Join(args, " "))
|
|
return nil
|
|
}}
|
|
for _, s := range w.plan() {
|
|
if err := s.do(); err != nil {
|
|
t.Fatalf("%s: %v", s.what, err)
|
|
}
|
|
}
|
|
for _, gone := range []string{"/etc/llmgw", "/var/lib/llmgw", "/var/lib/llmgw-agent",
|
|
"/etc/systemd/system/llmgw-server.service", "/etc/sudoers.d/llmgw-setup", "/etc/keepalived/keepalived.conf"} {
|
|
if _, err := os.Stat(filepath.Join(root, gone)); err == nil {
|
|
t.Errorf("%s is still there", gone)
|
|
}
|
|
}
|
|
for _, kept := range []string{"/srv/models/qwen/v1/q.gguf", "/opt/llmgw/engines", "/etc/patroni/config.yml",
|
|
"/var/lib/postgresql/16/llmgw", "/var/lib/etcd/default", "/usr/local/bin/llmgw"} {
|
|
if _, err := os.Stat(filepath.Join(root, kept)); err != nil {
|
|
t.Errorf("%s was removed without being asked", kept)
|
|
}
|
|
}
|
|
all := strings.Join(ran, "\n")
|
|
for _, want := range []string{"systemctl disable --now llmgw-server.service", "pkill -KILL -u llmgw-engine",
|
|
"nft delete table inet llmgw", "systemctl disable --now keepalived", "userdel llmgw-engine", "userdel llmgw"} {
|
|
if !strings.Contains(all, want) {
|
|
t.Errorf("did not run %q:\n%s", want, all)
|
|
}
|
|
}
|
|
if strings.Contains(all, "patroni") {
|
|
t.Error("the store was touched without --store")
|
|
}
|
|
}
|
|
|
|
func TestWipeAllTakesTheStoreAndTheModelsToo(t *testing.T) {
|
|
root := wipeFixture(t)
|
|
w := &wiper{root: root, all: true, run: func(string, ...string) error { return nil }}
|
|
for _, s := range w.plan() {
|
|
if err := s.do(); err != nil {
|
|
t.Fatalf("%s: %v", s.what, err)
|
|
}
|
|
}
|
|
for _, gone := range []string{"/srv/models", "/opt/llmgw", "/etc/patroni", "/var/lib/postgresql/16/llmgw",
|
|
"/var/lib/etcd/default", "/etc/default/etcd", "/usr/local/bin/llmgw"} {
|
|
if _, err := os.Stat(filepath.Join(root, gone)); err == nil {
|
|
t.Errorf("%s is still there", gone)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWipeLeavesAKeepalivedItDidNotWrite(t *testing.T) {
|
|
root := t.TempDir()
|
|
p := filepath.Join(root, "/etc/keepalived/keepalived.conf")
|
|
_ = os.MkdirAll(filepath.Dir(p), 0o755)
|
|
_ = os.WriteFile(p, []byte("vrrp_instance site_vip {}\n"), 0o600)
|
|
w := &wiper{root: root, run: func(string, ...string) error { return nil }}
|
|
for _, s := range w.plan() {
|
|
_ = s.do()
|
|
}
|
|
if _, err := os.Stat(p); err != nil {
|
|
t.Error("someone else's keepalived configuration was removed")
|
|
}
|
|
}
|