From af214078fd067f578d393665c451ae3f9af2401e Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Fri, 27 Oct 2023 14:35:00 +0100 Subject: [PATCH] Add a GET /node/primary (#5793) --- CHANGELOG.md | 1 + doc/schemas/node_openapi.json | 15 ++++++++++++++- src/node/rpc/node_frontend.h | 26 +++++++++++++++++++++++--- tests/e2e_common_endpoints.py | 6 ++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a902d5817451..1cff406895ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - In governance contexts, JS runtimes now only use runtime limits from the [public:ccf.gov.js_runtime_options map](https://microsoft.github.io/CCF/main/audit/builtin_maps.html#js-runtime-options) if they are strictly higher than the defaults (#5730). - Fixed an issue where a JS runtime limit could be hit out of user code execution, leading to an incorrectly constructed JS runtime or a crash (#5730). +- Added a GET /node/primary endpoint, returning 200 when primary and 404 when not, for load balancers to use (#5789). ## [5.0.0-dev4] diff --git a/doc/schemas/node_openapi.json b/doc/schemas/node_openapi.json index a16c6d677bbe..b55e5a87c82f 100644 --- a/doc/schemas/node_openapi.json +++ b/doc/schemas/node_openapi.json @@ -905,7 +905,7 @@ "info": { "description": "This API provides public, uncredentialed access to service and node state.", "title": "CCF Public Node API", - "version": "4.6.0" + "version": "4.7.0" }, "openapi": "3.0.0", "paths": { @@ -1365,6 +1365,19 @@ } }, "/node/primary": { + "get": { + "responses": { + "200": { + "description": "Default response description" + }, + "default": { + "$ref": "#/components/responses/default" + } + }, + "x-ccf-forwarding": { + "$ref": "#/components/x-ccf-forwarding/never" + } + }, "head": { "responses": { "200": { diff --git a/src/node/rpc/node_frontend.h b/src/node/rpc/node_frontend.h index 4bd2de5577c4..7324792227be 100644 --- a/src/node/rpc/node_frontend.h +++ b/src/node/rpc/node_frontend.h @@ -383,7 +383,7 @@ namespace ccf openapi_info.description = "This API provides public, uncredentialed access to service and node " "state."; - openapi_info.document_version = "4.6.0"; + openapi_info.document_version = "4.7.0"; } void init_handlers() override @@ -1232,7 +1232,7 @@ namespace ccf .set_auto_schema() .install(); - auto is_primary = [this](auto& args) { + auto head_primary = [this](auto& args) { if (this->node_operation.can_replicate()) { args.rpc_ctx->set_response_status(HTTP_STATUS_OK); @@ -1276,7 +1276,27 @@ namespace ccf } }; make_read_only_endpoint( - "/primary", HTTP_HEAD, is_primary, no_auth_required) + "/primary", HTTP_HEAD, head_primary, no_auth_required) + .set_forwarding_required(endpoints::ForwardingRequired::Never) + .install(); + + auto get_primary = [this](auto& args) { + if (this->node_operation.can_replicate()) + { + args.rpc_ctx->set_response_status(HTTP_STATUS_OK); + return; + } + else + { + args.rpc_ctx->set_error( + HTTP_STATUS_NOT_FOUND, + ccf::errors::ResourceNotFound, + "Node is not primary"); + return; + } + }; + make_read_only_endpoint( + "/primary", HTTP_GET, get_primary, no_auth_required) .set_forwarding_required(endpoints::ForwardingRequired::Never) .install(); diff --git a/tests/e2e_common_endpoints.py b/tests/e2e_common_endpoints.py index 7272a61ba4f4..ceda583b1f6c 100644 --- a/tests/e2e_common_endpoints.py +++ b/tests/e2e_common_endpoints.py @@ -17,6 +17,8 @@ def test_primary(network, args): with primary.client() as c: r = c.head("/node/primary") assert r.status_code == http.HTTPStatus.OK.value + r = c.get("/node/primary") + assert r.status_code == http.HTTPStatus.OK.value backup = network.find_any_backup() for interface_name in backup.host.rpc_interfaces.keys(): @@ -31,6 +33,10 @@ def test_primary(network, args): LOG.info( f'Successfully redirected to {r.headers["location"]} on primary {primary.local_node_id}' ) + r = c.get("/node/primary", allow_redirects=False) + assert r.status_code == http.HTTPStatus.NOT_FOUND.value, r + assert r.body.json()["error"]["code"] == "ResourceNotFound" + assert r.body.json()["error"]["message"] == "Node is not primary" return network