Skip to content

Commit

Permalink
Merge branch 'main' into refactor/liblogger-vlogf-vstring-function
Browse files Browse the repository at this point in the history
  • Loading branch information
hparzych authored Nov 3, 2023
2 parents 1f34d32 + 24fd122 commit 2943f28
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 92 deletions.
2 changes: 1 addition & 1 deletion .github/actions/build/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ runs:
steps:
- name: Build
shell: bash
run: cmake --build ${{ env.CMAKE_BUILD_DIR }}
run: cmake --build ${{ env.BUILD_PATH }}
2 changes: 1 addition & 1 deletion .github/actions/configure/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ runs:
run: >-
cmake
-S .
-B ${{ env.CMAKE_BUILD_DIR }}
-B ${{ env.BUILD_PATH }}
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
-DCMAKE_C_COMPILER=gcc
'-DCMAKE_CXX_COMPILER=g++'
Expand Down
3 changes: 1 addition & 2 deletions .github/actions/set-environment-variables/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ runs:
run: |
buildType=${{ inputs.build-type }}
printf "BUILD_TYPE=%s\n" "${buildType}" >> $GITHUB_ENV
printf "CMAKE_BUILD_DIR=%s\n" "cmake-build-${buildType,,}" >> $GITHUB_ENV
newestVersion="$(git describe --tags --abbrev=0 --always)"
if printf "%s" "${newestVersion}" | grep -qEv "[0-9]+\.[0-9]+\.[0-9]+"; then
Expand All @@ -25,7 +24,7 @@ runs:
newestVersionWithTimestamp="${newestVersion}"."$(date +%Y%m%d-%H%M%S)"
printf "ARTIFACT_VERSION=%s\n" "${newestVersionWithTimestamp}" >> $GITHUB_ENV
printf "RELEASE_PATH=%s\n" "${{ github.workspace }}/build/Release" >> $GITHUB_ENV
printf "BUILD_PATH=%s\n" "${{ github.workspace }}/cmake-build-${buildType,,}" >> $GITHUB_ENV
printf "ARTIFACT_PATH=%s\n" "${{ github.workspace }}/artifact" >> $GITHUB_ENV
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/test/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ runs:
steps:
- name: Test
shell: bash
run: ctest --test-dir ${{ env.CMAKE_BUILD_DIR }}
run: ctest --test-dir ${{ env.BUILD_PATH }}
1 change: 0 additions & 1 deletion .github/actions/update-packages/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ runs:
shell: bash
run: |
sudo apt-get update
sudo apt-get upgrade -y
5 changes: 0 additions & 5 deletions .github/workflows/build-and-test-debug.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ jobs:
with:
build-type: Debug

- name: Set environment variables
uses: ./.github/actions/set-environment-variables
with:
build-type: Debug

- name: Install requirements
uses: ./.github/actions/install-requirements

Expand Down
65 changes: 3 additions & 62 deletions .github/workflows/build-test-and-publish-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,72 +41,13 @@ jobs:
run: mv ${{ github.workspace }}/LICENSE ${{ env.ARTIFACT_PATH }}/LICENSE

- name: Move third party licenses to artifact directory
run: |
downloadLicense() {
local requirement="${1}"
local requirementPath="${2}"
local licenseUrl="${3}"
printf "[INFO] Downloading %s license file from %s to %s\n" "${requirement}" "${licenseUrl}" "${requirementPath}"
(cd "${requirementPath}" && curl "${licenseUrl}" --output LICENSE)
}
copyLicense() {
local requirement="${1}"
local requirementPath="${2}"
local licensePath="${3}"
printf "[INFO] Copying %s license file %s to %s\n" "${requirement}" "${licensePath}" "${requirementPath}"
cp "${licensePath}" "${requirementPath}"
}
licenseDirPaths="$(sudo find /home/runner/.conan2/p/ -type d -name "licenses" | tr "\n" " ")"
requirements="$(cat conanfile.txt | tr "\n" " " | grep -Po "\[requires\] \K[^\[]+")"
for requirement in $requirements; do
if printf "%s" "${requirement}" | grep -q "gtest/"; then
continue
fi
requirementPath=${{ env.ARTIFACT_PATH }}/thirdparty/${requirement}
mkdir -p "${requirementPath}"
if printf "%s" "${requirement}" | grep -q "protobuf/"; then
downloadLicense "${requirement}" "${requirementPath}" "https://raw.githubusercontent.com/protocolbuffers/protobuf/main/LICENSE"
continue
fi
cacheDirectoryPrefix="$(printf "%s" "${requirement}" | cut -d'/' -f1 | cut -c1-5)"
licenseDirPath="$(printf "%s" "${licenseDirPaths}" | grep -o "/home/runner/.conan2/p/[^ ]*${cacheDirectoryPrefix}[^ ]*/licenses")"
licensePath="${licenseDirPath}/LICENSE"
if [ -e "${licensePath}" ]; then
copyLicense "${requirement}" "${requirementPath}" "${licensePath}"
continue
fi
licensePath="${licenseDirPath}/LICENSE_1_0.txt"
if [ -e "${licensePath}" ]; then
copyLicense "${requirement}" "${requirementPath}" "${licensePath}"
continue
fi
licensePath="${licenseDirPath}/LICENSE.rst"
if [ -e "${licensePath}" ]; then
copyLicense "${requirement}" "${requirementPath}" "${licensePath}"
continue
fi
printf "[ERROR] No license file for %s has been found in %s\n" "${requirement}" "${licenseDirPath}"
return 1
done
run: mv ${{ env.BUILD_PATH }}/licenses ${{ env.ARTIFACT_PATH }}/thirdparty

- name: Move protobuf model to artifact directory
run: mv ${{ env.RELEASE_PATH }}/libebpfdiscoveryproto/ebpfdiscoveryproto ${{ env.ARTIFACT_PATH }}/ebpfdiscoveryproto
run: mv ${{ env.BUILD_PATH }}/libebpfdiscoveryproto/ebpfdiscoveryproto ${{ env.ARTIFACT_PATH }}/ebpfdiscoveryproto

- name: Move binaries to artifact directory
run: mv ${{ env.RELEASE_PATH }}/bin ${{ env.ARTIFACT_PATH }}/bin
run: mv ${{ env.BUILD_PATH }}/bin ${{ env.ARTIFACT_PATH }}/bin

- name: Remove test binaries from artifact directory
run: find ${{ env.ARTIFACT_PATH }}/* -name 'test*' -exec rm {} \;
Expand Down
28 changes: 27 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@ else ()
)
endif ()

function (collect_licenses conanfile)
file(READ ${conanfile} conanfile_contents)
string(REGEX MATCH "\\[requires\\](.*)" requires_section ${conanfile_contents})
string(REPLACE ";" "\n" requires_section ${CMAKE_MATCH_1})
string(REGEX MATCHALL "([^\n]+)" requirements_list ${requires_section})
foreach(requirement IN LISTS requirements_list)
string(REGEX MATCHALL "(.*)\/(.*)" requirement_match ${requirement})
if (NOT requirement_match)
break()
endif ()
set(requirement_name ${CMAKE_MATCH_1})
set(requirement_version ${CMAKE_MATCH_2})
string(TOUPPER ${requirement_name} requirement_name_uppercase)
set(requirement_license_dir "${CONAN_${requirement_name_uppercase}_ROOT}/licenses")
file(GLOB requirement_licenses "${requirement_license_dir}/*")
if (requirement_licenses)
message(STATUS "License file for ${requirement} found ${requirement_licenses}")
else ()
message(FATAL_ERROR "Cannot find any license files in ${requirement_license_dir}")
endif ()
file(COPY ${requirement_licenses} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/licenses/${requirement}/")
endforeach()
endfunction()

collect_licenses(conanfile.txt)

set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)

if (BUILD_TESTS OR BUILD_BPF_TESTS)
Expand Down Expand Up @@ -100,4 +126,4 @@ add_subdirectory(liblogging)

include(GNUInstallDirs)
add_subdirectory(ebpfdiscoverysrv)
install(TARGETS ebpfdiscoverysrv ebpfdiscoverysrv DESTINATION ${CMAKE_INSTALL_BINDIR})
install(TARGETS ebpfdiscoverysrv ebpfdiscoverysrv DESTINATION ${CMAKE_INSTALL_BINDIR})
11 changes: 3 additions & 8 deletions libebpfdiscovery/headers/ebpfdiscovery/IpAddressChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ struct IpIfce {
bool isLocalBridge;
};

class IpAddressCheckerInerface {
public:
virtual bool isAddressExternalLocal(IPv4int addr) = 0;
};

class IpAddressChecker : public IpAddressCheckerInerface {
class IpAddressChecker {
std::vector<IpIfce> interfaces;
std::vector<IpIfce>::iterator bridgeEnd = interfaces.end();
const NetlinkCalls& netlink;
Expand All @@ -31,14 +26,14 @@ class IpAddressChecker : public IpAddressCheckerInerface {
bool isLoopback(const IpIfce&);
void addIpIfce(IpIfce&& ifce);
void markBridge(int idx);

void printInfo();
protected:
void moveBridges();

public:
IpAddressChecker(const NetlinkCalls& calls);
IpAddressChecker(std::initializer_list<IpIfce> config, const NetlinkCalls& calls);
bool isAddressExternalLocal(IPv4int addr) override;
virtual bool isAddressExternalLocal(IPv4int addr);
bool readNetworks();
};
} // namespace ebpfdiscovery
20 changes: 18 additions & 2 deletions libebpfdiscovery/src/IpAddressChecker.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
#include "ebpfdiscovery/IpAddressChecker.h"

#include "logging/Logger.h"
#include <algorithm>
#include <arpa/inet.h>
#include <array>
Expand Down Expand Up @@ -80,7 +80,7 @@ bool IpAddressChecker::readNetworks() {
} else {
bridgeEnd = interfaces.end();
}

printInfo();
return ret;
}

Expand Down Expand Up @@ -139,6 +139,15 @@ static bool handleNetlink(S send, R receive, P parse, int domain) {
return true;
}

void IpAddressChecker::printInfo() {
char buff[16];
for (const auto& ifce : interfaces) {
for (const auto& ip : ifce.ip) {
LOG_INFO("IfceIdx:{} Ip:{} isLocalBridge:{}", ifce.index, inet_ntop(AF_INET, &ip, buff, sizeof(buff)), ifce.isLocalBridge);
}
}
}

bool IpAddressChecker::readAllIpAddrs() {
return handleNetlink(
[this](int fd, sockaddr_nl* sa, int domain) { return netlink.sendIpAddrRequest(fd, sa, AF_INET); },
Expand Down Expand Up @@ -186,6 +195,13 @@ bool IpAddressChecker::isAddressExternalLocal(IPv4int addr) {
return false;
}

const bool srcLocal = std::any_of(bridgeEnd, interfaces.end(), [addr](const auto& it) {
return std::any_of(it.ip.begin(), it.ip.end(), [addr](const auto& ip) { return addr == ip; });
});

if (srcLocal) {
return false;
}
const bool bridgeRelated = std::any_of(interfaces.begin(), bridgeEnd, [addr](const auto& it) {
return std::any_of(it.ip.begin(), it.ip.end(), [addr, mask = it.mask](const auto& ip) { return (addr & mask) == (ip & mask); });
});
Expand Down
6 changes: 6 additions & 0 deletions libebpfdiscovery/test/IpAddressCheckerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ TEST(TestAddressChecker, NOTLocalBridgeIp) {
EXPECT_TRUE(u.isAddressExternalLocal(inet_addr("10.3.34.2")));
}

TEST(TestAddressChecker, LocalIfceIpSrc) {
const NetlinkCallsMock netlinkMock;
IpAddressCheckerTest u({{{inet_addr("10.2.6.5")}, {}, 0x0000ffff, 0, false}}, netlinkMock);
EXPECT_FALSE(u.isAddressExternalLocal(inet_addr("10.2.6.5")));
}

TEST(TestAddressChecker, SimpleClassATest) {
NetlinkCalls calls;
IpAddressCheckerTest u(calls);
Expand Down
6 changes: 3 additions & 3 deletions libservice/headers/service/Aggregator.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace service {

class Aggregator {
public:
Aggregator(ebpfdiscovery::IpAddressCheckerInerface& ipChecker);
Aggregator(ebpfdiscovery::IpAddressChecker& ipChecker);

std::vector<Service> popServices();

Expand All @@ -38,9 +38,9 @@ class Aggregator {

using ServiceKey = std::pair<uint32_t, std::string>;
std::unordered_map<ServiceKey, Service> services;
ebpfdiscovery::IpAddressCheckerInerface& ipChecker;
ebpfdiscovery::IpAddressChecker& ipChecker;

std::atomic<bool> locked{false};
};

} // namespace service
} // namespace service
5 changes: 3 additions & 2 deletions libservice/src/Aggregator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

namespace service {

Aggregator::Aggregator(ebpfdiscovery::IpAddressCheckerInerface& ipChecker) : ipChecker(ipChecker) {
Aggregator::Aggregator(ebpfdiscovery::IpAddressChecker& ipChecker) : ipChecker(ipChecker) {
ipChecker.readNetworks();
}

void Aggregator::updateServiceClientsNumber(Service& service, const DiscoverySessionMeta& meta) {
Expand Down Expand Up @@ -69,4 +70,4 @@ std::vector<Service> Aggregator::popServices() {
return ret;
}

} // namespace service
} // namespace service
8 changes: 5 additions & 3 deletions libservice/test/AggregatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
using namespace ebpfdiscovery;
using namespace service;

class IpAddressCheckerMock : public IpAddressCheckerInerface {
class IpAddressCheckerMock : public IpAddressChecker {
public:
using IpAddressChecker::IpAddressChecker;
MOCK_METHOD(bool, isAddressExternalLocal, (IPv4int), (override));
};

Expand All @@ -29,7 +30,8 @@ struct ServiceAggregatorTest : public testing::Test {
return {request, meta};
}

IpAddressCheckerMock ipCheckerMock;
const NetlinkCalls netlink;
IpAddressCheckerMock ipCheckerMock{netlink};
Aggregator aggregator{ipCheckerMock};
};

Expand Down Expand Up @@ -80,4 +82,4 @@ TEST_F(ServiceAggregatorTest, aggregate) {
EXPECT_THAT(services, testing::Contains(expectedService3));
}
EXPECT_EQ(aggregator.popServices().size(), 0);
}
}
46 changes: 46 additions & 0 deletions testing/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import pytest
import subprocess
import glob

from utils import discovered_service_has_clients, is_responsive
from time import sleep


def pytest_addoption(parser):
parser.addoption("--discovery_path", action="store", help="Path to eBPF Discovery binary")


@pytest.fixture(scope="session")
def discovery_path(pytestconfig):
discovery_path = pytestconfig.getoption("discovery_path")
assert discovery_path, "Path to eBPF discovery needs to be provided via --discovery_path"
return discovery_path


@pytest.fixture(scope="session")
def run_ebpf_discovery(discovery_path):
args = (discovery_path, "--interval", "2")
discovery = subprocess.Popen(args, stdout=subprocess.PIPE)
yield discovery

discovery.terminate()
while discovery.poll() is None:
sleep(0.5)
exit_code = discovery.returncode
assert not exit_code, "Discovery returned exit code: {}".format(exit_code)

discovery_root_dir = os.path.dirname(os.path.realpath(discovery_path))
log_files = glob.glob(discovery_root_dir+'/*.log')
assert log_files == [], "Discovery produced log files on exit: {}".format(log_files)


@pytest.fixture(scope="session")
def http_service(docker_ip, docker_services, run_ebpf_discovery):
port = docker_services.port_for("httpbin", 80)
url = "http://{}:{}/".format(docker_ip, port)
docker_services.wait_until_responsive(
timeout=30.0, pause=0.1, check=lambda: is_responsive(url)
)
assert discovered_service_has_clients(run_ebpf_discovery, url, 1, 0)
return url
3 changes: 3 additions & 0 deletions testing/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest~=7.4.3
pytest-docker~=2.0.0
requests~=2.31.0
13 changes: 13 additions & 0 deletions testing/test_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from utils import discovered_service_has_clients, send_http_requests


def test_service_discovery(run_ebpf_discovery, http_service):
url = http_service + "some/url"
requests_num = 5
send_http_requests(url, requests_num)
assert discovered_service_has_clients(run_ebpf_discovery, url, requests_num, 0)

url = http_service + "other/url"
requests_num = 10
send_http_requests(url, requests_num)
assert discovered_service_has_clients(run_ebpf_discovery, url, requests_num, 0)
Loading

0 comments on commit 2943f28

Please sign in to comment.