Skip to content

Commit

Permalink
Remove protobuf depenency
Browse files Browse the repository at this point in the history
Original motivation for this change was to
remove unnecessary binary dependency on
libatomic. As it appeared source of this dependency
was protobuf. As this quite complex piece of
software was used only to generate simple json
I swiched it to boost::json.
I decided to use boost::json because boost already was
dependency for eBPF discovery.
  • Loading branch information
karczex committed Dec 17, 2024
1 parent 1ec4c44 commit 015453f
Show file tree
Hide file tree
Showing 17 changed files with 341 additions and 326 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ if (CMAKE_BUILD_TYPE STREQUAL "Release")
endif ()

if ("${PROJECT_VERSION}" STREQUAL "")
message(FATAL_ERROR "Project version has not been provided\n Please, provide project version using -DPROJECT_VERSION")
set(PROJECT_VERSION 0.0.0)
message(WARNING "Project version has not been provided so it is set to default value: ${PROJECT_VERSION}\n Please, provide project version using -DPROJECT_VERSION")
endif ()

string(REGEX MATCH "([0-9]+)\.([0-9]+)\.([0-9]+)" _ "${PROJECT_VERSION}")
Expand Down Expand Up @@ -126,7 +127,6 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libbpf-boo

add_subdirectory(third_party)
add_subdirectory(libebpfdiscovery)
add_subdirectory(libebpfdiscoveryproto)
add_subdirectory(libebpfdiscoveryshared)
add_subdirectory(libebpfdiscoveryskel)
add_subdirectory(libhttpparser)
Expand Down
1 change: 0 additions & 1 deletion conanfile.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[requires]
protobuf/3.21.9
ms-gsl/4.0.0
fmt/10.1.1
spdlog/1.12.0
Expand Down
4 changes: 4 additions & 0 deletions ebpfdiscoverysrv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ add_executable(${TARGET} ${SOURCES})
target_link_libraries(${TARGET} PRIVATE ebpfdiscovery)
target_link_options(${TARGET} PRIVATE "-static-libgcc" "-static-libstdc++")
target_compile_definitions(${TARGET} PUBLIC PROJECT_VERSION="${PROJECT_VERSION}")

if (BUILD_TESTS)
add_test(NAME binary_dependencies COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test/dependencies.sh $<TARGET_FILE:${TARGET}>)
endif ()
11 changes: 11 additions & 0 deletions ebpfdiscoverysrv/test/dependencies.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

binary_path="${1}"
approved_binary_dependencies=("libelf|libz|libm|libdl|librt|libpthread|libc|ld-linux")

depenencies=$(objdump -p "${binary_path}" 2>/dev/null | grep NEEDED | grep -Ev "${approved_binary_dependencies}")

if [ ! -z "${depenencies}" ]; then
echo "Additional dependencies: ${depenencies}"
exit 1
fi
3 changes: 1 addition & 2 deletions libebpfdiscovery/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ target_include_directories(${TARGET} PRIVATE src PUBLIC headers)
target_link_libraries(${TARGET}
PUBLIC
libpf-tools
ebpfdiscoveryproto
ebpfdiscoveryshared
ebpfdiscoveryskel
httpparser
Expand All @@ -41,7 +40,7 @@ target_link_libraries(${TARGET}
)

if (BUILD_TESTS)
list(APPEND TEST_SOURCES test/LRUCacheTest.cpp)
list(APPEND TEST_SOURCES test/LRUCacheTest.cpp test/JsonTest.cpp)
set(TEST_TARGET test${TARGET})

add_executable(${TEST_TARGET} ${TEST_SOURCES})
Expand Down
73 changes: 73 additions & 0 deletions libebpfdiscovery/headers/ebpfdiscovery/Json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* Copyright 2023 Dynatrace LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <boost/json.hpp>
#include <chrono>
#include <string_view>
#include <unordered_map>

namespace boost::json {
template <typename T>
void tag_invoke(json::value_from_tag, json::value& v, std::reference_wrapper<T> const& wrapped) {
v = json::value_from(wrapped.get());
}
} // namespace boost::json

namespace boost::json::ext {

void print(std::ostream& os, boost::json::value const& jv) {
switch (jv.kind()) {
case json::kind::object: {
auto const& obj = jv.get_object();
if (!obj.empty()) {
os << "{";
for (auto it = obj.begin(); it != obj.end(); ++it) {
auto val = it->value();
if (val.is_null() || (val.is_string() && (val.get_string().size() == 0))) {
continue;
}
if (it != obj.begin()) {
os << ",";
}
os << json::serialize(it->key()) << ":";
print(os, val);
}
os << "}";
}
break;
}

case json::kind::array: {
auto const& arr = jv.get_array();
if (!arr.empty()) {
os << "[";
auto it = arr.begin();
for (auto it = arr.begin(); it != arr.end(); ++it) {
if (it != arr.begin()) {
os << ",";
}
print(os, *it);
}
}
os << "]";
break;
}
default:
os << jv;
}
}
} // namespace boost::json::ext
17 changes: 5 additions & 12 deletions libebpfdiscovery/src/Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include "ebpfdiscovery/Discovery.h"

#include "ebpfdiscovery/Session.h"
#include "ebpfdiscoveryproto/Translator.h"
#include "ebpfdiscovery/Json.h"
#include "logging/Logger.h"
#include "service/IpAddress.h"

Expand All @@ -32,6 +32,7 @@
#include <string>
#include <string_view>
#include <thread>
#include <iostream>

namespace ebpfdiscovery {

Expand Down Expand Up @@ -62,17 +63,9 @@ void Discovery::outputServicesToStdout() {
return;
}

ServicesList servicesProto{};
bool isListEmpty = false;
{
std::lock_guard<std::mutex> lock(serviceAggregator.getServicesMutex());
std::tie(servicesProto, isListEmpty) = proto::internalToProto(services, serviceAggregator.getEnableNetworkCounters());
}
if (!isListEmpty) {
const auto servicesJson{proto::protoToJson(servicesProto)};
std::cout << servicesJson << std::endl;
}

boost::json::object outJson{{"services", boost::json::value_from(services)}};
boost::json::ext::print(std::cout, outJson);

serviceAggregator.clear();
}

Expand Down
186 changes: 186 additions & 0 deletions libebpfdiscovery/test/JsonTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright 2023 Dynatrace LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ebpfdiscovery/Json.h"
#include <gtest/gtest.h>

#include "service/Service.h"
#include <iostream>
#include <string>
#include <string_view>
#include <vector>

#include <boost/describe.hpp>

class JsonTest : public testing::Test {};

struct testClass {
std::string str = "foo";
std::string empty = "";
};

BOOST_DESCRIBE_STRUCT(testClass, (), (str, empty))

bool is_parsable_back(std::string_view json_string) {
boost::system::error_code ec;
boost::json::parse(json_string, ec);
if (ec) {
return false;
}
return true;
}

TEST_F(JsonTest, removeEmptyKeys) {

std::vector<testClass> vtc(4, {"bar", ""});
boost::json::object json{{"key", boost::json::value_from(vtc)}};

std::stringstream result;
boost::json::ext::print(result, json);

const std::string expected{"{\"key\":[{\"str\":\"bar\"},{\"str\":\"bar\"},{\"str\":\"bar\"},{\"str\":\"bar\"}]}"};

EXPECT_TRUE(is_parsable_back(result.str()));
EXPECT_EQ(result.str(), expected);
}

TEST_F(JsonTest, servicesToJson) {

std::vector<std::reference_wrapper<service::Service>> internalServices;
service::Service service1{.pid = 1, .endpoint = "/endpoint/1", .internalClientsNumber = 1, .externalClientsNumber = 2};
service::Service service2{.pid = 2, .endpoint = "/endpoint/1", .internalClientsNumber = 1, .externalClientsNumber = 2};
service::Service service3{.pid = 3, .endpoint = "/endpoint/2", .internalClientsNumber = 1, .externalClientsNumber = 2};

service::Service service4{
.pid = 4,
.endpoint = "google.com/endpoint/3",
.domain = "google.com",
.scheme = "http",
.internalClientsNumber = 1,
.externalClientsNumber = 2};
service::Service service5{
.pid = 5,
.endpoint = "dynatrace.com/endpoint/4",
.domain = "dynatrace.com",
.scheme = "https",
.internalClientsNumber = 1,
.externalClientsNumber = 2};

internalServices.push_back(service1);
internalServices.push_back(service2);
internalServices.push_back(service3);
internalServices.push_back(service4);
internalServices.push_back(service5);

boost::json::object outJson{{"service", boost::json::value_from(internalServices)}};

std::stringstream result;
boost::json::ext::print(result, outJson);

// clang-format off
const std::string expected{"{\"service\":["
"{\"pid\":1,\"endpoint\":\"/endpoint/1\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},"
"{\"pid\":2,\"endpoint\":\"/endpoint/1\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},"
"{\"pid\":3,\"endpoint\":\"/endpoint/2\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},"
"{\"pid\":4,\"endpoint\":\"google.com/endpoint/3\",\"domain\":\"google.com\",\"scheme\":\"http\",\"internalClientsNumber\":1,\"externalClientsNumber\":2},"
"{\"pid\":5,\"endpoint\":\"dynatrace.com/endpoint/4\",\"domain\":\"dynatrace.com\",\"scheme\":\"https\",\"internalClientsNumber\":1,\"externalClientsNumber\":2}]}"};
// clang-format on
EXPECT_TRUE(is_parsable_back(result.str()));
EXPECT_EQ(result.str(), expected);
}

TEST_F(JsonTest, servicesToJsonNetworkCounters) {
std::unordered_map<uint32_t, std::chrono::time_point<std::chrono::steady_clock>> detectedExternalIPv416Networks = {
{0xAC8F, std::chrono::steady_clock::now()}, {0xACC7, std::chrono::steady_clock::now()}};
std::unordered_map<uint32_t, std::chrono::time_point<std::chrono::steady_clock>> detectedExternalIPv424Networks = {
{0xAC8F04, std::chrono::steady_clock::now()},
{0xAC8F06, std::chrono::steady_clock::now()},
{0xACC72D, std::chrono::steady_clock::now()}};
std::unordered_map<
std::array<uint8_t, service::ipv6NetworkPrefixBytesLen>,
std::chrono::time_point<std::chrono::steady_clock>,
service::ArrayHasher>
externalIPv6ClientsNets = {
{{0x20, 0x01, 0x48, 0x60, 0x48, 0x60}, std::chrono::steady_clock::now()},
{{0x12, 0x34, 0x23, 0x45, 0x34, 0x56}, std::chrono::steady_clock::now()}};

std::vector<std::reference_wrapper<service::Service>> internalServices;
service::Service service1{
.pid = 1,
.endpoint = "/endpoint/1",
.internalClientsNumber = 1,
.externalClientsNumber = 3,
.externalIPv4_16ClientNets = detectedExternalIPv416Networks,
.externalIPv4_24ClientNets = detectedExternalIPv424Networks};
service::Service service2{
.pid = 2,
.endpoint = "/endpoint/1",
.internalClientsNumber = 1,
.externalClientsNumber = 3,
.externalIPv4_16ClientNets = detectedExternalIPv416Networks,
.externalIPv4_24ClientNets = detectedExternalIPv424Networks};
service::Service service3{
.pid = 3,
.endpoint = "/endpoint/2",
.internalClientsNumber = 1,
.externalClientsNumber = 3,
.externalIPv4_16ClientNets = detectedExternalIPv416Networks,
.externalIPv4_24ClientNets = detectedExternalIPv424Networks};

service::Service service4{
.pid = 4,
.endpoint = "google.com/endpoint/3",
.domain = "google.com",
.scheme = "http",
.internalClientsNumber = 1,
.externalClientsNumber = 2,
.externalIPv6ClientsNets = externalIPv6ClientsNets};
service::Service service5{
.pid = 5,
.endpoint = "dynatrace.com/endpoint/4",
.domain = "dynatrace.com",
.scheme = "https",
.internalClientsNumber = 1,
.externalClientsNumber = 2,
.externalIPv6ClientsNets = externalIPv6ClientsNets};

internalServices.emplace_back(service1);
internalServices.emplace_back(service2);
internalServices.emplace_back(service3);
internalServices.emplace_back(service4);
internalServices.emplace_back(service5);

boost::json::object outJson{{"service", boost::json::value_from(internalServices)}};

std::stringstream result;
boost::json::ext::print(result, outJson);
const std::string expected{"{\"service\":[{\"pid\":1,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_"
"24ClientNets\":3},{\"pid\":2,\"endpoint\":\"/endpoint/"
"1\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_"
"24ClientNets\":3},{\"pid\":3,\"endpoint\":\"/endpoint/"
"2\",\"internalClientsNumber\":1,\"externalClientsNumber\":3,\"externalIPv4_16ClientNets\":2,\"externalIPv4_"
"24ClientNets\":3},{\"pid\":4,\"endpoint\":\"google.com/"
"endpoint/"
"3\",\"domain\":\"google.com\",\"scheme\":\"http\",\"internalClientsNumber\":1,\"externalClientsNumber\":2,"
"\"externalIPv6ClientsNets\":2},{\"pid\":5,\"endpoint\":\"dynatrace.com/"
"endpoint/"
"4\",\"domain\":\"dynatrace.com\",\"scheme\":\"https\",\"internalClientsNumber\":1,"
"\"externalClientsNumber\":2,\"externalIPv6ClientsNets\":2}]}"};

EXPECT_TRUE(is_parsable_back(result.str()));
EXPECT_EQ(result.str(), expected);
}
Loading

0 comments on commit 015453f

Please sign in to comment.