REST API rendszer infóval: stackctl

A werkstatt sorozat második része – egy REST API ami rendszer információt és konténer státuszt szolgáltat HTTP-n.

REST API rendszer infóval: stackctl

Az envcheck után az volt a kérdés: hogyan lépek tovább a fájl-beolvasástól a hálózati kommunikációig? A stackctl erre ad választ – egy REST API ami a szerver állapotát teszi elérhetővé HTTP-n keresztül.

A gondolat onnan jött, hogy a szervereimet távolról szerettem volna lekérdezni: mennyi a memória, mekkora a lemezhasználat, milyen konténerek futnak. SSH-val persze meg lehet nézni, de egy curl paranccsal egyszerűbb – és scriptelhető.

Mit csinál?

Három végpont van. A /health egy egyszerű életjel – ha válaszol, a szerver fut. A /info visszaadja a rendszer információt: uname, uptime, memória, lemezhasználat. A /stacks pedig a futó konténereket listázza JSON formátumban.

Indítás után a stackctl egy porton figyel és vár a kérésekre:

stackctl -port 9090 -runtime docker

Egy másik terminálból:

curl localhost:9090/health
{"status":"ok","runtime":"docker"}

curl localhost:9090/info
{"system":"Linux srv 6.1.0 ...","uptime":"up 12 days",...}

Ami új volt a Go-ban

Az envcheck-ben fájlokat olvastam be. A stackctl-ben HTTP kéréseket szolgálok ki – és meglepően kevés kód kell hozzá.

A Go net/http csomagja három sorban ad egy működő webszervert. A http.HandleFunc regisztrál egy útvonalat, a http.ListenAndServe elindítja a szervert. Nincs framework, nincs konfiguráció, nincs middleware lánc. Csak a standard library.

A másik új elem az os/exec csomag. Ezzel futtatok shell parancsokat Go-ból – uname -a, free -h, df -h /. Lényegében ugyanaz mint bash-ben a $(uname -a), csak Go-ban a kimenetet explicit kell kezelni. Az exec.Command visszaad egy objektumot, az .Output() metódus futtatja és visszaadja az eredményt byte tömbként.

Handler függvények

Az eredeti verzióban az összes handler a main() függvényben volt anonim függvényként. A production ready verzióban ezeket kiszerveztem nevesített függvényekbe:

func healthHandler(cfg config) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        writeJSON(w, HealthResponse{
            Status:  "ok",
            Runtime: cfg.runtime,
        })
    }
}

Ez a minta – egy függvény ami visszaad egy másik függvényt – először furcsa volt. De van értelme: a külső függvény megkapja a konfigurációt, a belső pedig a HTTP kérést. Így a handler ismeri a beállításokat anélkül hogy globális változókat használnánk.

JSON válaszok

Az eredeti verzióban kézzel raktam össze a JSON stringeket fmt.Sprintf-fel. Ez törékeny – egy elírt idézőjel és érvénytelen a JSON. A production ready verzióban struct-okat definiálok és a json.NewEncoder sorosítja őket:

type InfoResponse struct {
    System string `json:"system"`
    Uptime string `json:"uptime"`
    Memory string `json:"memory"`
    Disk   string `json:"disk"`
}

A backtick-es tag-ek (json:"system") mondják meg a Go-nak hogy a struct mező milyen néven jelenjen meg a JSON kimenetben. Ez a Go módja a serializációnak – nincs annotation, nincs dekorátor, csak struct tag-ek.

Tesztelés HTTP nélkül

A Go net/http/httptest csomagja lehetővé teszi hogy HTTP handlereket tesztelj anélkül hogy ténylegesen elindítanád a szervert. Egy httptest.NewRecorder() szimulálja a HTTP választ, egy httptest.NewRequest() pedig a kérést:

req := httptest.NewRequest("GET", "/health", nil)
rec := httptest.NewRecorder()
handler(rec, req)

Ez után a rec.Code a HTTP státusz kód, a rec.Body pedig a válasz tartalma. Nincs port, nincs hálózat, nincs várakozás – a teszt milliszekundum alatt lefut.

Mit tanultam?

A stackctl három alapvető Go koncepciót tanított meg:

A HTTP szerver a net/http csomaggal – hogyan regisztrálj útvonalakat, hogyan írj JSON választ, hogyan kezeld a hibákat HTTP szinten.

A shell parancs futtatás az os/exec-kel – hogyan hívj meg külső programokat, hogyan kapd el a kimenetüket, és hogyan kezeld ha nem sikerül.

A handler minta – függvények amik függvényeket adnak vissza, és a httptest csomag amivel mindezt teszteled szerver indítás nélkül.

Hogyan próbáld ki?

git clone https://github.com/brtkcs/werkstatt-tools.git
cd werkstatt-tools/stackctl
go run main.go

Egy másik terminálból:

curl localhost:8080/health
curl localhost:8080/info
curl localhost:8080/stacks

A teljes forráskód a werkstatt-tools repóban.

Ez a werkstatt sorozat második része. Az első részben az envcheck-ről írtam, egy .env validátorról. Most egy HTTP szerverre ugrunk – a stackctl a Go net/http csomagjával és az os/exec-kel dolgozik.