DEV Community

flisk
flisk

Posted on

Bonus: /total Returns the Goose Function, /health Stays 200

Part 5 of Goose, then any language on Flisk. Part 4 already proved the container. This part uses the function from part 2 on a second path. Flisk's command does not change. docs/byo.md still only requires PORT, 0.0.0.0, and HTTP 200 on /health.

0*0 + 1*1 + 2*2 + 3*3 + 4*4 is 30. That number should come from total, not from a literal in C.

Host check, before Docker

Keep shop.goose from part 3 and run it on the host. It exits. The server file must not.

goose shop.goose
Enter fullscreen mode Exit fullscreen mode

You want the array [0, 1, 4, 9, 16] and the sum 30. If that is wrong, fix the function here. Do not debug it through Traefik.

server.goose

serve now takes the sum. i32 is enough for 30, and it matches the port as i32 call you already have.

fn total(xs: i64[:]) -> i64 {
    var t = 0;
    for x in xs { t += x; }
    return t;
}

fn main() {
    var grow: i64[>..] = [];
    for i in 5 { grow.push(i * i); }
    let sum = total(grow);
    print(grow);
    print(sum);

    var text: u8[>..] = [];
    let found = os.env("PORT", text);
    let port, parsed = parse_int(text);
    if !found || !parsed || port <= 0 || port > 65535 {
        abort("PORT missing or not a port");
    }
    let code = serve(port as i32, sum as i32);
    if code != 0 { abort("serve failed"); }
}

extern fn serve(port: i32, sum: i32) -> i32;
Enter fullscreen mode Exit fullscreen mode

print still runs once, before accept. Look for 30 in the container log. If the log says 30 and /total says something else, the bug is in http.c, not in Goose.

http.c

/health stays the Flisk probe. /total is the extra path. The prefix is stripped, so the process never sees /api/goose/total.

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int serve(int port, int sum) {
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) return 1;
    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons((unsigned short)port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) return 2;
    if (listen(fd, 16) != 0) return 3;
    for (;;) {
        int client = accept(fd, 0, 0);
        if (client < 0) continue;
        char req[1024];
        int n = (int)read(client, req, sizeof(req) - 1);
        if (n < 0) n = 0;
        req[n] = 0;

        char body;
        int is_health = strncmp(req, "GET /health", 11) == 0;
        int is_total = strncmp(req, "GET /total", 10) == 0;
        if (is_health) {
            snprintf(body, sizeof(body), "{\"status\":\"ok\"}\n");
        } else if (is_total) {
            snprintf(body, sizeof(body), "{\"total\":%d}\n", sum);
        }

        char resp;
        if (is_health || is_total) {
            snprintf(resp, sizeof(resp),
                "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n%s",
                strlen(body), body);
        } else {
            const char *missing = "not found\n";
            snprintf(resp, sizeof(resp),
                "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n%s",
                strlen(missing), missing);
        }
        (void)write(client, resp, strlen(resp));
        close(client);
    }
}
Enter fullscreen mode Exit fullscreen mode

Do not register the resource again. service.json did not change. There is no live reload for this type, so the new binary is a new image.

cd services/shop/goose-api
docker build -t goose-health .
docker run --rm -d --name goose-health -e PORT=4000 -p 4000:4000 goose-health
curl -fsS http://127.0.0.1:4000/health
curl -fsS http://127.0.0.1:4000/total
docker logs goose-health
docker stop goose-health
Enter fullscreen mode Exit fullscreen mode

Expect {"status":"ok"}, then {"total":30}. Then restart the stack you already registered:

flisk up shop
curl -fsS http://api.<project>.localhost/api/goose/health
curl -fsS http://api.<project>.localhost/api/goose/total
Enter fullscreen mode Exit fullscreen mode

Use the host --dry-run printed in part 4. A 200 on /health with a 404 on /total means Traefik is fine and the old image is still running.

Change Touch this Do not touch
New path server.goose, http.c, then rebuild service.json, unless the health path changed
Probe /health must stay 200 --restart no
Route inside the container, /total /api/goose/total in the C strncmp
Next language a different Dockerfile, same flisk resource flags --language goose

Swap Fastify, Gin, or Java into that directory and the Flisk line from part 4 is still the line. Only the files behind CMD change.

Top comments (0)