Compose stackek kezelése egy helyről: deployer
Öt-hat compose stack fut a szerveremen. Vaultwarden, Gitea, Immich, és a többiek. Mindegyiknek saját mappája van, saját docker-compose.yml-je. Ha az összeset le akarom állítani, öt mappába kell belépnem és öt podman compose down-t futtatnom.
A deployer ezt oldja meg. Egy YAML fájlban definiálom az összes stacket, és egyetlen paranccsal kezelhetem mindet – vagy egyet kiválasztva.
deployer status # mi fut, mi nem?
deployer up vaultwarden # egy stacket indít
deployer restart # mindet újraindít
A konfig
A deployer.yaml egyszerű:
runtime: podman
stacks:
- name: vaultwarden
path: /opt/stacks/vaultwarden
- name: gitea
path: /opt/stacks/gitea
- name: immich
path: /opt/stacks/immich
A runtime mező mondja meg hogy podman vagy docker – a parancsok ugyanazok, csak a bináris neve más. Ez fontos, mert a homelab-em Podman-t futtat, de a munkahelyi szerverek Docker-t.
YAML és Go
A Go-ban a YAML kezelés a gopkg.in/yaml.v3 csomaggal történik. Ez az első külső dependency a werkstatt sorozatban – az eddigi projektek mind a standard library-vel dolgoztak.
A struct tag-ek mondják meg a YAML csomagnak hogyan párosítsa a mezőket:
type Stack struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
}
Az yaml.Unmarshal beolvassa a bájtokat és feltölti a struct-ot. Ha a YAML hibás, hibát ad vissza – nem csendben eltűnik mint bash-ben egy rosszul parse-olt konfig.
A runner interface
Az eredeti verzióban az exec.Command közvetlenül a main() függvényben hívódott. Ez működik, de tesztelhetetlen – nem akarok valódi compose parancsokat futtatni a tesztekben.
A megoldás egy interface:
type runner interface {
run(runtime, dir string, args ...string) (string, error)
runAttached(runtime, dir string, args ...string) error
}
Két implementáció van. Az execRunner a valódi, ami ténylegesen futtatja a parancsokat. A mockRunner a tesztekben használt, ami előre megadott kimenetet ad vissza:
type mockRunner struct {
output string
err error
}
Ez a Go módja a dependency injection-nek. Nincs framework, nincs annotation, nincs DI container. Egy interface, két implementáció, és a run() függvény megkapja paraméterként hogy melyiket használja.
Konfig validáció
Az eredeti verzió nem ellenőrizte a konfigurációt – ha hiányzott egy mező, futásidőben kapott hibát. A production ready verzióban a validateConfig függvény induláskor ellenőriz mindent:
Van-e legalább egy stack? Van-e minden stacknek neve? Van-e minden stacknek path-ja? Ha bármi hibás, értelmesebb hibaüzenet jön mint egy “nil pointer dereference”.
Ez apróságnak tűnik, de a valódi eszközöknél a felhasználói élmény nagy része az, hogy a hibaüzenetek segítenek a problémát megtalálni.
A subcommand minta
Az envcheck-ben és a netmapper-ben a flag-ek az egyetlen bemeneti mód. A deployer-ben subcommand-ok is vannak: status, up, down, restart. A Go flag csomag közvetlenül nem támogatja a subcommand-okat, de a flag.Args() visszaadja a flag-ek utáni argumentumokat:
flag.Parse()
args := flag.Args()
action := args[0] // "status", "up", "down", "restart"
target := args[1] // opcionális stack név
A switch action pedig az egyes akcióknak megfelelő kódot futtatja. Egyszerűbb mint egy cobra vagy urfave/cli framework – és egy négy parancsot kezelő eszköznél tökéletesen elég.
Tesztelés mock-kal
A deployer tesztjei nem indítanak konténereket. A mock runner előre beállított kimeneteket ad, és a tesztek azt ellenőrzik hogy a logika helyesen dolgozza-e fel:
A futó stack [{"Name":"test","State":"running"}] kimenetet ad – a stackStatus ezt “running”-ként azonosítja. Az üres kimenet vagy [] “stopped”. A parancs hiba “error”.
Ez 12 teszt ami lefed: konfig betöltés, validáció, szűrés, státusz ellenőrzés, és exit code-ok. Az egész 0.008 másodperc alatt fut.
Mit tanultam?
A deployer a négy production ready projekt közül a legösszetettebb, és három fontos Go mintát tanított meg:
Az interface-alapú dependency injection – hogyan válaszd szét a logikát a végrehajtástól, hogy tesztelhető legyen. A YAML konfiguráció kezelés – hogyan használj külső csomagot és hogyan validáld a bemenetet. A subcommand minta – hogyan építs egy CLI eszközt ami több akciót is tud kezelni.
Hogyan próbáld ki?
git clone https://github.com/brtkcs/werkstatt-tools.git
cd werkstatt-tools/deployer
go mod tidy
go build -o deployer
Hozz létre egy deployer.yaml-t a saját stackjeidhez és:
./deployer status
./deployer up vaultwarden
./deployer restart
A teljes forráskód a werkstatt-tools repóban.