Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Healthcheck endpoint #1751

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/app/ClioApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@

namespace {

auto constexpr HealthCheckHTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Test page for Clio</title></head>
<body><h1>Clio Test</h1><p>This page shows Clio http(s) connectivity is working.</p></body>
</html>
)html";

/**
* @brief Start context threads
*
Expand Down Expand Up @@ -178,6 +186,16 @@
}
);

httpServer->onGet(
godexsoft marked this conversation as resolved.
Show resolved Hide resolved
"/health",
kuznetsss marked this conversation as resolved.
Show resolved Hide resolved
[](web::ng::Request const& request,

Check warning on line 191 in src/app/ClioApplication.cpp

View check run for this annotation

Codecov / codecov/patch

src/app/ClioApplication.cpp#L191

Added line #L191 was not covered by tests
web::ng::ConnectionMetadata&,
web::SubscriptionContextPtr,
boost::asio::yield_context) -> web::ng::Response {
return web::ng::Response{boost::beast::http::status::ok, HealthCheckHTML, request};
}
);

util::Logger webServerLog{"WebServer"};
auto onRequest = [adminVerifier, &webServerLog, &handler](
web::ng::Request const& request,
Expand Down
5 changes: 1 addition & 4 deletions src/etlng/impl/GrpcSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#include <cstddef>
#include <cstdint>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>
Expand All @@ -49,10 +48,8 @@ resolve(std::string const& ip, std::string const& port)
{
web::Resolver resolver;

if (auto const results = resolver.resolve(ip, port); not results.empty()) {
std::cout << "resolved ip: '" << results.at(0) << '\n';
godexsoft marked this conversation as resolved.
Show resolved Hide resolved
if (auto const results = resolver.resolve(ip, port); not results.empty())
return results.at(0);
}

throw std::runtime_error("Failed to resolve " + ip + ":" + port);
}
Expand Down
11 changes: 11 additions & 0 deletions src/web/impl/HttpBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@

namespace web::impl {

static auto constexpr HealthCheckHTML = R"html(
<!DOCTYPE html>
<html>
<head><title>Test page for Clio</title></head>
<body><h1>Clio Test</h1><p>This page shows Clio http(s) connectivity is working.</p></body>
</html>
)html";

using tcp = boost::asio::ip::tcp;

/**
Expand Down Expand Up @@ -207,6 +215,9 @@ class HttpBase : public ConnectionBase {
if (ec)
return httpFail(ec, "read");

if (req_.method() == http::verb::get and req_.target() == "/health")
return sender_(httpResponse(http::status::ok, "text/html", HealthCheckHTML));

// Update isAdmin property of the connection
ConnectionBase::isAdmin_ = adminVerification_->isAdmin(req_, this->clientIp);

Expand Down
9 changes: 5 additions & 4 deletions tests/common/util/TestHttpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <boost/beast/http.hpp> // IWYU pragma: keep
#include <boost/beast/http/field.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/status.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/beast/http/write.hpp> // IWYU pragma: keep
Expand All @@ -58,7 +59,7 @@ using tcp = boost::asio::ip::tcp;

namespace {

std::string
std::pair<boost::beast::http::status, std::string>
godexsoft marked this conversation as resolved.
Show resolved Hide resolved
syncRequest(
std::string const& host,
std::string const& port,
Expand Down Expand Up @@ -96,7 +97,7 @@ syncRequest(
boost::beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);

return res.body();
return {res.result(), res.body()};
}

} // namespace
Expand All @@ -105,7 +106,7 @@ WebHeader::WebHeader(http::field name, std::string value) : name(name), value(st
{
}

std::string
std::pair<boost::beast::http::status, std::string>
HttpSyncClient::post(
std::string const& host,
std::string const& port,
Expand All @@ -116,7 +117,7 @@ HttpSyncClient::post(
return syncRequest(host, port, body, std::move(additionalHeaders), http::verb::post);
}

std::string
std::pair<boost::beast::http::status, std::string>
HttpSyncClient::get(
std::string const& host,
std::string const& port,
Expand Down
6 changes: 4 additions & 2 deletions tests/common/util/TestHttpClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/http/field.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/status.hpp>
#include <boost/beast/http/string_body.hpp>

#include <chrono>
#include <expected>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

struct WebHeader {
Expand All @@ -43,15 +45,15 @@ struct WebHeader {
};

struct HttpSyncClient {
static std::string
static std::pair<boost::beast::http::status, std::string>
post(
std::string const& host,
std::string const& port,
std::string const& body,
std::vector<WebHeader> additionalHeaders = {}
);

static std::string
static std::pair<boost::beast::http::status, std::string>
get(std::string const& host,
std::string const& port,
std::string const& body,
Expand Down
52 changes: 43 additions & 9 deletions tests/unit/web/ServerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <string>
#include <string_view>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -214,8 +215,9 @@ TEST_F(WebServerTest, Http)
{
auto e = std::make_shared<EchoExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const res = HttpSyncClient::post("localhost", port, R"({"Hello":1})");
auto const [status, res] = HttpSyncClient::post("localhost", port, R"({"Hello":1})");
EXPECT_EQ(res, R"({"Hello":1})");
EXPECT_EQ(status, boost::beast::http::status::ok);
}

TEST_F(WebServerTest, Ws)
Expand All @@ -233,11 +235,12 @@ TEST_F(WebServerTest, HttpInternalError)
{
auto e = std::make_shared<ExceptionExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const res = HttpSyncClient::post("localhost", port, R"({})");
auto const [status, res] = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(
res,
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
);
EXPECT_EQ(status, boost::beast::http::status::internal_server_error);
}

TEST_F(WebServerTest, WsInternalError)
Expand Down Expand Up @@ -316,13 +319,16 @@ TEST_F(WebServerTest, HttpRequestOverload)
{
auto e = std::make_shared<EchoExecutor>();
auto const server = makeServerSync(cfg, ctx, dosGuardOverload, e);
auto res = HttpSyncClient::post("localhost", port, R"({})");
auto [status, res] = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(res, "{}");
res = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(status, boost::beast::http::status::ok);

std::tie(status, res) = HttpSyncClient::post("localhost", port, R"({})");
EXPECT_EQ(
res,
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
);
EXPECT_EQ(status, boost::beast::http::status::service_unavailable);
}

TEST_F(WebServerTest, WsRequestOverload)
Expand All @@ -349,11 +355,12 @@ TEST_F(WebServerTest, HttpPayloadOverload)
std::string const s100(100, 'a');
auto e = std::make_shared<EchoExecutor>();
auto server = makeServerSync(cfg, ctx, dosGuardOverload, e);
auto const res = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
auto const [status, res] = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
EXPECT_EQ(
res,
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
);
EXPECT_EQ(status, boost::beast::http::status::ok);
}

TEST_F(WebServerTest, WsPayloadOverload)
Expand Down Expand Up @@ -393,6 +400,26 @@ TEST_F(WebServerTest, WsTooManyConnection)
EXPECT_TRUE(exceptionThrown);
}

TEST_F(WebServerTest, HealthCheck)
{
auto e = std::make_shared<ExceptionExecutor>(); // request handled before we get to executor
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/health");

EXPECT_FALSE(res.empty());
EXPECT_EQ(status, boost::beast::http::status::ok);
}

TEST_F(WebServerTest, GetOtherThanHealthCheck)
{
auto e = std::make_shared<ExceptionExecutor>(); // request handled before we get to executor
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/");

EXPECT_FALSE(res.empty());
EXPECT_EQ(status, boost::beast::http::status::bad_request);
}

std::string
JSONServerConfigWithAdminPassword(uint32_t const port)
{
Expand Down Expand Up @@ -500,8 +527,11 @@ TEST_P(WebServerAdminTest, HttpAdminCheck)
auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e);
std::string const request = "Why hello";
uint32_t const webServerPort = serverConfig.value<uint32_t>("server.port");
auto const res = HttpSyncClient::post("localhost", std::to_string(webServerPort), request, GetParam().headers);
auto const [status, res] =
HttpSyncClient::post("localhost", std::to_string(webServerPort), request, GetParam().headers);

EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
EXPECT_EQ(status, boost::beast::http::status::ok);
}

INSTANTIATE_TEST_CASE_P(
Expand Down Expand Up @@ -618,8 +648,10 @@ TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
uint32_t const webServerPort = tests::util::generateFreePort();
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics");
auto const [status, res] = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics");

EXPECT_EQ(res, "Only admin is allowed to collect metrics");
EXPECT_EQ(status, boost::beast::http::status::unauthorized);
}

TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
Expand All @@ -641,7 +673,7 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
PrometheusService::init(serverConfig);
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::get(
auto const [status, res] = HttpSyncClient::get(
"localhost",
std::to_string(webServerPort),
"",
Expand All @@ -652,6 +684,7 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
)}
);
EXPECT_EQ(res, "Prometheus is disabled in clio config");
EXPECT_EQ(status, boost::beast::http::status::forbidden);
}

TEST_F(WebServerPrometheusTest, validResponse)
Expand All @@ -662,7 +695,7 @@ TEST_F(WebServerPrometheusTest, validResponse)
auto e = std::make_shared<EchoExecutor>();
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
auto const res = HttpSyncClient::get(
auto const [status, res] = HttpSyncClient::get(
"localhost",
std::to_string(webServerPort),
"",
Expand All @@ -673,4 +706,5 @@ TEST_F(WebServerPrometheusTest, validResponse)
)}
);
EXPECT_EQ(res, "# TYPE test_counter counter\ntest_counter 1\n\n");
EXPECT_EQ(status, boost::beast::http::status::ok);
}
Loading