Skip to content

Commit

Permalink
feat: add a new unauthenticated /_ready HTTP endpoint (#2592)
Browse files Browse the repository at this point in the history
This PR adds a new _ready route, that allow orchestrators that uses probes to detect if the Kuzzle server is ready to accept incoming requests (node started, up and running and not overloaded)

This specific implementation allow this route to not be part of the right system of Kuzzle.

So that orchestrator can use this route to detect if Kuzzle is alive.
  • Loading branch information
alexandrebouthinon authored Feb 14, 2025
1 parent c33f0dc commit 52d54fa
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 2 deletions.
24 changes: 22 additions & 2 deletions doc/2/guides/main-concepts/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,29 @@ This endpoint is accessible with the route `GET /_healthcheck`.
curl "http://localhost:7512/_healthcheck"
```

This route does not require any authentication. It returns a `200` status code if the server is up and running.
This route does not require any authentication. It returns a `200` status code if the Kuzzle server is started.

This is useful when kuzzle is deployed inside a Kubernetes cluster and you want to configure probes to check the server.
This is useful when Kuzzle is deployed inside a Kubernetes cluster and you want to configure a [`livenessProbe`](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request) to check the Kuzzle server is started.

#### Ready endpoint

Kuzzle exposes a readiness endpoint.

This endpoint is accessible with the route `GET /_ready`.

```bash
curl "http://localhost:7512/_ready"
```

This route does not require any authentication. It returns a `200` status code if the server is running and ready to accept new incoming requests.
It returns a `503` status code if:
- The Kuzzle server state is one of the following:
- Node starting procedure has not finished yet.
- Node is shutting down.
- The cluster does not have enough nodes to be considered as healthy.
- The Kuzzle server is overloaded (meaning the number of requests in the queue is above the [configured threshold](https://github.com/kuzzleio/kuzzle/blob/c33f0dcb904b01941c0967450dc52848ba3456d7/.kuzzlerc.sample.jsonc#L92)).

This is useful when Kuzzle is deployed inside a Kubernetes cluster and you want to configure a [`readinessProbe`](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) to check the Kuzzle server is ready to accept new incoming requests.

---

Expand Down
24 changes: 24 additions & 0 deletions docker/scripts/start-kuzzle-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,30 @@ app.controller.register("tests", {
});
},
},
simulateOutage: {
http: [{ verb: "get", path: "/tests/simulate-outage" }],
handler: async (request: KuzzleRequest) => {
const outageType = request.getString("type");

switch (outageType) {
case "overload":
global.kuzzle.funnel.overloaded = true;
break;
case "nodeNotStarted":
global.kuzzle.state = 1;
break;
default:
break;
}
},
},
clearOutage: {
http: [{ verb: "get", path: "/tests/clear-outage" }],
handler: async () => {
global.kuzzle.funnel.overloaded = false;
global.kuzzle.state = 2;
},
},
},
});

Expand Down
42 changes: 42 additions & 0 deletions jest/network/network/ready.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import rp from "request-promise";

test("Check _ready result", async () => {
const response = rp.get({
uri: "http://localhost:17510/_ready",
});

await expect(response).resolves.not.toThrow(); // Should return 200
});

test("Check _ready during node startup", async () => {
await rp.get({
uri: "http://localhost:17510/tests/simulate-outage?type=nodeNotStarted",
});

const response = rp.get({
uri: "http://localhost:17510/_ready",
});

await expect(response).rejects; // Should return 503

await rp.get({
uri: "http://localhost:17510/tests/clear-outage",
});
});

test("Check _ready during node overload", async () => {
await rp.get({
uri: "http://localhost:17510/tests/simulate-outage?type=overload",
});

const response = rp.get({
uri: "http://localhost:17510/_ready",
});

await expect(response).rejects; // Should return 503

await rp.get({
uri: "http://localhost:17510/tests/clear-outage",
});
});

22 changes: 22 additions & 0 deletions lib/core/network/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const { Request } = require("../../api/request");
const kerror = require("../../kerror");
const HttpRouter = require("./httpRouter");
const { removeStacktrace } = require("../../util/stackTrace");
const kuzzleStateEnum = require("../../kuzzle/kuzzleStateEnum");

/**
* @class Router
Expand Down Expand Up @@ -148,6 +149,27 @@ class Router {
cb(request);
});

this.http.get("_ready", (request, cb) => {
let status = 200;

if (
global.kuzzle.state !== kuzzleStateEnum.RUNNING ||
global.kuzzle.funnel.overloaded
) {
status = 503;
}

request.response.configure({
status: status,
});

/**
* By avoiding using the funnel, we avoid the request to be logged
* This is useful for ochestrators healthchecks
*/
cb(request);
});

for (const route of routes) {
const verb = route.verb.toLowerCase();

Expand Down

0 comments on commit 52d54fa

Please sign in to comment.