Skip to content

Commit

Permalink
feat: Rest endoint /health for rln (#2011)
Browse files Browse the repository at this point in the history
* Rest endoint /health added

* Remove dev-debug echo

* Changed not ready message

* Update waku/node/rest/health/handlers.nim

Co-authored-by: Ivan Folgueira Bande <128452529+Ivansete-status@users.noreply.github.com>

* Various fixes on PR foundings, added comments

---------

Co-authored-by: Ivan Folgueira Bande <128452529+Ivansete-status@users.noreply.github.com>
  • Loading branch information
NagyZoltanPeter and Ivansete-status authored Sep 8, 2023
1 parent a8095d8 commit fc6194b
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 1 deletion.
4 changes: 4 additions & 0 deletions apps/wakunode2/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import
../../waku/node/rest/relay/topic_cache,
../../waku/node/rest/filter/handlers as rest_filter_api,
../../waku/node/rest/store/handlers as rest_store_api,
../../waku/node/rest/health/handlers as rest_health_api,
../../waku/node/jsonrpc/admin/handlers as rpc_admin_api,
../../waku/node/jsonrpc/debug/handlers as rpc_debug_api,
../../waku/node/jsonrpc/filter/handlers as rpc_filter_api,
Expand Down Expand Up @@ -569,6 +570,9 @@ proc startRestServer(app: App, address: ValidIpAddress, port: Port, conf: WakuNo
## Debug REST API
installDebugApiHandlers(server.router, app.node)

## Health REST API
installHealthApiHandler(server.router, app.node)

## Relay REST API
if conf.relay:
let relayCache = TopicCache.init(capacity=conf.restRelayCacheCapacity)
Expand Down
3 changes: 2 additions & 1 deletion tests/all_tests_waku.nim
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ when defined(rln):
./waku_rln_relay/test_waku_rln_relay,
./waku_rln_relay/test_wakunode_rln_relay,
./waku_rln_relay/test_rln_group_manager_onchain,
./waku_rln_relay/test_rln_group_manager_static
./waku_rln_relay/test_rln_group_manager_static,
./wakunode_rest/test_rest_health
77 changes: 77 additions & 0 deletions tests/wakunode_rest/test_rest_health.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{.used.}

import
std/tempfiles,
stew/shims/net,
testutils/unittests,
presto,
presto/client as presto_client,
libp2p/peerinfo,
libp2p/multiaddress,
libp2p/crypto/crypto
import
../../waku/waku_node,
../../waku/node/waku_node as waku_node2, # TODO: Remove after moving `git_version` to the app code.
../../waku/node/rest/server,
../../waku/node/rest/client,
../../waku/node/rest/responses,
../../waku/node/rest/health/handlers as health_api,
../../waku/node/rest/health/client as health_api_client,
../../waku/waku_rln_relay,
../testlib/common,
../testlib/wakucore,
../testlib/wakunode


proc testWakuNode(): WakuNode =
let
privkey = crypto.PrivateKey.random(Secp256k1, rng[]).tryGet()
bindIp = ValidIpAddress.init("0.0.0.0")
extIp = ValidIpAddress.init("127.0.0.1")
port = Port(0)

newTestWakuNode(privkey, bindIp, port, some(extIp), some(port))


suite "Waku v2 REST API - health":
asyncTest "Get node health info - GET /health":
# Given
let node = testWakuNode()
await node.start()
await node.mountRelay()

let restPort = Port(58001)
let restAddress = ValidIpAddress.init("0.0.0.0")
let restServer = RestServerRef.init(restAddress, restPort).tryGet()

installHealthApiHandler(restServer.router, node)
restServer.start()
let client = newRestHttpClient(initTAddress(restAddress, restPort))

# When
var response = await client.healthCheck()

# Then
check:
response.status == 503
$response.contentType == $MIMETYPE_TEXT
response.data == "Node is not ready"

# now kick in rln (currently the only check for health)
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode"),
))

# When
response = await client.healthCheck()

# Then
check:
response.status == 200
$response.contentType == $MIMETYPE_TEXT
response.data == "Node is healthy"

await restServer.stop()
await restServer.closeWait()
await node.stop()
30 changes: 30 additions & 0 deletions waku/node/rest/health/client.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}

import
chronicles,
json_serialization,
json_serialization/std/options,
presto/[route, client]
import
../serdes,
../responses

logScope:
topics = "waku node rest health_api"

proc decodeBytes*(t: typedesc[string], value: openArray[byte],
contentType: Opt[ContentTypeData]): RestResult[string] =
if MediaType.init($contentType) != MIMETYPE_TEXT:
error "Unsupported contentType value", contentType = contentType
return err("Unsupported contentType")

var res: string
if len(value) > 0:
res = newString(len(value))
copyMem(addr res[0], unsafeAddr value[0], len(value))
return ok(res)

proc healthCheck*(): RestResponse[string] {.rest, endpoint: "/health", meth: HttpMethod.MethodGet.}
42 changes: 42 additions & 0 deletions waku/node/rest/health/handlers.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}

import
chronicles,
json_serialization,
presto/route
import
../../waku_node,
../responses,
../serdes

logScope:
topics = "waku node rest health_api"

const ROUTE_HEALTH* = "/health"

const FutIsReadyTimout = 5.seconds

proc installHealthApiHandler*(router: var RestRouter, node: WakuNode) =
## /health endpoint provides information about node readiness to caller.
## Currently it is restricted to checking RLN (if mounted) proper setup
## TODO: Leter to extend it to a broader information about each subsystem state
## report. Rest response to change to JSON structure that can hold exact detailed
## information.

router.api(MethodGet, ROUTE_HEALTH) do () -> RestApiResponse:

let isReadyStateFut = node.isReady()
if not await isReadyStateFut.withTimeout(FutIsReadyTimout):
return RestApiResponse.internalServerError("Health check timed out")

var msg = "Node is healthy"
var status = Http200

if not isReadyStateFut.read():
msg = "Node is not ready"
status = Http503

return RestApiResponse.textResponse(msg, status)
41 changes: 41 additions & 0 deletions waku/node/rest/health/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
openapi: 3.0.3
info:
title: Waku V2 node REST API
version: 1.0.0
contact:
name: VAC Team
url: https://forum.vac.dev/

tags:
- name: health
description: Healt check REST API for WakuV2 node

paths:
/health:
get:
summary: Get node health status
description: Retrieve readiness of a Waku v2 node.
operationId: healthcheck
tags:
- health
responses:
'200':
description: Waku v2 node is up and running.
content:
text/plain:
schema:
type: string
example: Node is healty
'500':
description: Internal server error
content:
text/plain:
schema:
type: string
'503':
description: Node not initialized or having issues
content:
text/plain:
schema:
type: string
example: Node is not initialized

0 comments on commit fc6194b

Please sign in to comment.