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(libsinsp/container_engine): proper containerd support #2195

Merged
merged 14 commits into from
Jan 8, 2025
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
20 changes: 15 additions & 5 deletions test/libsinsp_e2e/container/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ TEST_F(sys_call_test, container_cgroups) {
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});
ASSERT_TRUE(done);
Expand Down Expand Up @@ -180,6 +181,7 @@ TEST_F(sys_call_test, container_clone_nspid) {
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});
ASSERT_TRUE(done);
Expand Down Expand Up @@ -233,6 +235,7 @@ TEST_F(sys_call_test, container_clone_nspid_ioctl) {
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});
ASSERT_TRUE(done);
Expand Down Expand Up @@ -312,6 +315,7 @@ static void run_container_docker_test(bool fork_after_container_start) {
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});
ASSERT_TRUE(done);
Expand Down Expand Up @@ -351,7 +355,7 @@ TEST_F(sys_call_test, container_docker_bad_socket) {
return;
}

before_capture_t setup = [&](sinsp* inspector) {
before_open_t setup = [&](sinsp* inspector) {
inspector->set_docker_socket_path("/invalid/path");
};

Expand Down Expand Up @@ -393,7 +397,7 @@ TEST_F(sys_call_test, container_docker_bad_socket) {
ASSERT_NE(PPME_CONTAINER_JSON_2_E, param.m_evt->get_type());

sinsp_threadinfo* tinfo = param.m_evt->get_thread_info(false);
ASSERT_TRUE(tinfo->m_container_id.length() == 12);
ASSERT_TRUE(tinfo->m_container_id.length() <= 12);
therealbobo marked this conversation as resolved.
Show resolved Hide resolved
ASSERT_TRUE(param.m_inspector->m_container_manager.container_exists(tinfo->m_container_id));
const auto container_info =
param.m_inspector->m_container_manager.get_container(tinfo->m_container_id);
Expand All @@ -407,7 +411,9 @@ TEST_F(sys_call_test, container_docker_bad_socket) {
inspector->set_docker_socket_path("/var/run/docker.sock");
};

ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); });
ASSERT_NO_FATAL_FAILURE({
event_capture::run(test, callback, filter, setup, event_capture::do_nothing, cleanup);
});
ASSERT_TRUE(done);
}

Expand All @@ -420,7 +426,7 @@ TEST_F(sys_call_test, container_libvirt) {
}

// Setup phase before capture has start, to avoid generating too many events
before_capture_t setup = [](sinsp* inspector) {
before_open_t setup = [](sinsp* inspector) {
FILE* f = fopen("/tmp/conf.xml", "w");
ASSERT_TRUE(f != NULL);
fprintf(f,
Expand Down Expand Up @@ -495,6 +501,7 @@ TEST_F(sys_call_test, container_libvirt) {
callback,
filter,
setup,
event_capture::do_nothing,
cleanup,
libsinsp::events::sinsp_state_sc_set());
});
Expand Down Expand Up @@ -643,6 +650,7 @@ static void healthcheck_helper(
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});

Expand Down Expand Up @@ -684,6 +692,7 @@ static void healthcheck_tracefile_helper(
filter,
event_capture::do_nothing,
event_capture::do_nothing,
event_capture::do_nothing,
libsinsp::events::sinsp_state_sc_set());
});

Expand Down Expand Up @@ -833,7 +842,7 @@ TEST_F(sys_call_test, docker_container_large_json) {

ASSERT_TRUE(dhelper.build_image() == 0);

before_capture_t before = [&](sinsp* inspector) {
before_open_t before = [&](sinsp* inspector) {
inspector->set_container_labels_max_len(60000);
};

Expand Down Expand Up @@ -892,6 +901,7 @@ TEST_F(sys_call_test, docker_container_large_json) {
callback,
filter,
before,
event_capture::do_nothing,
cleanup,
libsinsp::events::sinsp_state_sc_set());
});
Expand Down
135 changes: 131 additions & 4 deletions test/libsinsp_e2e/container/container_cri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,27 @@ void container_cri::fake_cri_test_timing(const std::string& pb_prefix,
EXPECT_FALSE(saw_container_callback)
<< "Received more than one on_new_container callback";

verify_container_info(tinfo->m_container_id, exp_info, container);
saw_container_callback = true;
// If the machine running tests has some other container running,
// we could receive a new container without a threadinfo.
if(tinfo) {
verify_container_info(tinfo->m_container_id, exp_info, container);
saw_container_callback = true;
}
});
};

before_capture_t cleanup = [&](sinsp* inspector) {
inspector->set_docker_socket_path(default_docker_socket);
};

EXPECT_NO_FATAL_FAILURE(
{ event_capture::run(test, container_event_callback, filter, setup, cleanup); });
EXPECT_NO_FATAL_FAILURE({
event_capture::run(test,
container_event_callback,
filter,
setup,
event_capture::do_nothing,
cleanup);
});

// We only expect to see a container event when the lookup succeeds
if(exp_info.state == sinsp_container_lookup::state::SUCCESSFUL) {
Expand Down Expand Up @@ -549,3 +559,120 @@ TEST_F(container_cri, fake_cri_fail_sync) {
exp_info,
1 << CT_CONTAINERD);
}

TEST_F(container_cri, fake_cri_multiple) {
auto pb_prefix = LIBSINSP_TEST_RESOURCES_PATH "/fake_cri_falco";
auto alt_pb_prefix = LIBSINSP_TEST_RESOURCES_PATH "/fake_cri_multi";
std::atomic<bool> done(false);
std::atomic<bool> done_alt(false);
const std::string alt_cri_container_id = "593f5b76be2a";

auto runtime = "containerd";

unlink(fake_cri_socket.c_str());
subprocess fake_cri_handle(LIBSINSP_TEST_PATH "/fake_cri/fake_cri",
{"unix://" + fake_cri_socket, pb_prefix, runtime});
pid_t fake_cri_pid = fake_cri_handle.get_pid();

std::string alt_fake_cri_socket = "/tmp/alt_fake_cri.sock";

unlink(alt_fake_cri_socket.c_str());
subprocess alt_fake_cri_handle(
LIBSINSP_TEST_PATH "/fake_cri/fake_cri",
{"unix://" + alt_fake_cri_socket, alt_pb_prefix, runtime, alt_cri_container_id});
pid_t alt_fake_cri_pid = alt_fake_cri_handle.get_pid();

auto start_time = time(NULL);

event_filter_t filter = [&](sinsp_evt* evt) {
return evt->get_type() == PPME_CONTAINER_JSON_E ||
evt->get_type() == PPME_CONTAINER_JSON_2_E;
};

run_callback_t test = [&](sinsp* inspector) {
subprocess handle(LIBSINSP_TEST_PATH "/test_helper", {"cri_container_echo"});
handle.in() << "\n";
handle.wait();
subprocess alt_handle(LIBSINSP_TEST_PATH "/test_helper",
{"cri_container_echo",
"593f5b76be2afc23c39aa7eaa29174eac353d32be5e006b710c01aacca4aa05e"});
alt_handle.in() << "\n";
alt_handle.wait();
while(!done && !done_alt && time(NULL) < start_time + 10) {
usleep(100000);
}
};

captured_event_callback_t cri_callback = [&](const callback_param& param) {
sinsp_threadinfo* tinfo = param.m_evt->get_tinfo();
EXPECT_TRUE(tinfo != NULL);

if(tinfo->m_container_id == cri_container_id) {
EXPECT_EQ(cri_container_id, tinfo->m_container_id);

const auto container_info =
param.m_inspector->m_container_manager.get_container(tinfo->m_container_id);
EXPECT_NE(container_info, nullptr);

EXPECT_EQ(sinsp_container_type::CT_CONTAINERD, container_info->m_type);
EXPECT_EQ("falco", container_info->m_name);
EXPECT_EQ("docker.io/falcosecurity/falco:latest", container_info->m_image);
EXPECT_EQ("sha256:8d0619a4da278dfe2772f75aa3cc74df0a250385de56085766035db5c9a062ed",
container_info->m_imagedigest);
EXPECT_EQ("4bc0e14060f4263acf658387e76715bd836a13b9ba44f48465bd0633a412dbd0",
container_info->m_imageid);
EXPECT_EQ(1073741824, container_info->m_memory_limit);
EXPECT_EQ(102, container_info->m_cpu_shares);
EXPECT_EQ(0, container_info->m_cpu_quota);
EXPECT_EQ(100000, container_info->m_cpu_period);
done = true;
} else {
EXPECT_EQ(alt_cri_container_id, tinfo->m_container_id);

const auto container_info =
param.m_inspector->m_container_manager.get_container(tinfo->m_container_id);
EXPECT_NE(container_info, nullptr);

EXPECT_EQ(sinsp_container_type::CT_CONTAINERD, container_info->m_type);
EXPECT_EQ("falco-2", container_info->m_name);
EXPECT_EQ("docker.io/falcosecurity/falco:latest", container_info->m_image);
EXPECT_EQ("sha256:4df3aba7463d88aefbab4eb9e241468b0475f5e8c2c138d4cd811ca812975612",
container_info->m_imagedigest);
EXPECT_EQ("74d48ff156776f5fc1c625d72163eb539e63967bc87baf9158cdaca218c39465",
container_info->m_imageid);
EXPECT_EQ(1073741824, container_info->m_memory_limit);
EXPECT_EQ(102, container_info->m_cpu_shares);
EXPECT_EQ(0, container_info->m_cpu_quota);
EXPECT_EQ(100000, container_info->m_cpu_period);
done_alt = true;
}
};

before_capture_t setup = [&](sinsp* inspector) {
inspector->set_docker_socket_path("");
inspector->set_cri_socket_path(fake_cri_socket);
inspector->add_cri_socket_path(alt_fake_cri_socket);
inspector->set_cri_extra_queries(true);
};

after_capture_t cleanup = [&](sinsp* inspector) {
inspector->set_docker_socket_path(default_docker_socket);
};

EXPECT_NO_FATAL_FAILURE({ event_capture::run(test, cri_callback, filter, setup, cleanup); });

// The fake server had to stay running the whole time in order
// for the test to be succesful
// Needed to reap the zombine if it exited
waitpid(fake_cri_pid, NULL, WNOHANG);
EXPECT_TRUE(fake_cri_handle.is_alive());

waitpid(alt_fake_cri_pid, NULL, WNOHANG);
EXPECT_TRUE(alt_fake_cri_handle.is_alive());

EXPECT_TRUE(done);
EXPECT_TRUE(done_alt);

fake_cri_handle.kill();
alt_fake_cri_handle.kill();
}
11 changes: 7 additions & 4 deletions test/libsinsp_e2e/event_capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ std::string event_capture::s_engine_path;
unsigned long event_capture::s_buffer_dim = DEFAULT_DRIVER_BUFFER_BYTES_DIM * 4;

event_capture::event_capture(captured_event_callback_t captured_event_callback,
before_capture_t before_open,
after_capture_t before_close,
before_open_t before_open,
before_capture_t before_capture,
after_capture_t after_capture,
event_filter_t filter,
uint32_t max_thread_table_size,
uint64_t thread_timeout_ns,
uint64_t inactive_thread_scan_time_ns) {
m_captured_event_callback = std::move(captured_event_callback);
m_before_capture = std::move(before_open);
m_after_capture = std::move(before_close);
m_before_open = std::move(before_open);
m_before_capture = std::move(before_capture);
m_after_capture = std::move(after_capture);
m_filter = std::move(filter);

m_eventfd = -1;
Expand Down Expand Up @@ -68,6 +70,7 @@ void event_capture::start(bool dump, libsinsp::events::set<ppm_sc_code>& sc_set)
}
}
}
m_before_open(m_inspector.get());
therealbobo marked this conversation as resolved.
Show resolved Hide resolved
open_engine(event_capture::get_engine(), sc_set);

const ::testing::TestInfo* const test_info =
Expand Down
28 changes: 18 additions & 10 deletions test/libsinsp_e2e/event_capture.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class callback_param {
sinsp* m_inspector;
};

// Right before inspector->open_*() gets called.
typedef std::function<void(sinsp* inspector)> before_open_t;
// Right before inspector->start_capture() gets called.
// Engine is already opened (thus scap handle is already alive).
typedef std::function<void(sinsp* inspector)> before_capture_t;
Expand All @@ -62,8 +64,9 @@ typedef std::function<void()> run_callback_async_t;
class event_capture {
public:
event_capture(captured_event_callback_t captured_event_callback,
before_capture_t before_open,
after_capture_t before_close,
before_open_t before_open,
before_capture_t before_capture,
after_capture_t after_capture,
event_filter_t filter,
uint32_t max_thread_table_size,
uint64_t thread_timeout_ns,
Expand All @@ -81,22 +84,24 @@ class event_capture {
and, for any event that matches the filter,
calls captured_event_callback.
Before starting the capture, before_open is called.
After closing the capture, before_close is called.
After closing the capture, after_capture is called.
The default ppm_sc_set is the whole set minus `read` and `readv`.
*/
static void run(const run_callback_t& run_function,
captured_event_callback_t captured_event_callback,
event_filter_t filter,
before_capture_t before_open = event_capture::do_nothing,
after_capture_t before_close = event_capture::do_nothing,
before_open_t before_open = event_capture::do_nothing,
before_capture_t before_capture = event_capture::do_nothing,
after_capture_t after_capture = event_capture::do_nothing,
libsinsp::events::set<ppm_sc_code> sc_set = {},
uint32_t max_thread_table_size = 131072,
uint64_t thread_timeout_ns = (uint64_t)60 * 1000 * 1000 * 1000,
uint64_t inactive_thread_scan_time_ns = (uint64_t)60 * 1000 * 1000 * 1000,
bool dump = true) {
event_capture capturing(std::move(captured_event_callback),
std::move(before_open),
std::move(before_close),
std::move(before_capture),
std::move(after_capture),
std::move(filter),
max_thread_table_size,
thread_timeout_ns,
Expand All @@ -119,22 +124,24 @@ class event_capture {
and, for any event that matches the filter,
calls captured_event_callback.
Before starting the capture, before_open is called.
After closing the capture, before_close is called.
After closing the capture, after_capture is called.
The default ppm_sc_set is the whole set minus `read` and `readv`.
*/
static void run(const run_callback_async_t& run_function,
captured_event_callback_t captured_event_callback,
event_filter_t filter,
before_capture_t before_open = event_capture::do_nothing,
after_capture_t before_close = event_capture::do_nothing,
before_open_t before_open = event_capture::do_nothing,
before_capture_t before_capture = event_capture::do_nothing,
after_capture_t after_capture = event_capture::do_nothing,
libsinsp::events::set<ppm_sc_code> sc_set = {},
uint32_t max_thread_table_size = 131072,
uint64_t thread_timeout_ns = (uint64_t)60 * 1000 * 1000 * 1000,
uint64_t inactive_thread_scan_time_ns = (uint64_t)60 * 1000 * 1000 * 1000,
bool dump = true) {
event_capture capturing(std::move(captured_event_callback),
std::move(before_open),
std::move(before_close),
std::move(before_capture),
std::move(after_capture),
std::move(filter),
max_thread_table_size,
thread_timeout_ns,
Expand Down Expand Up @@ -176,6 +183,7 @@ class event_capture {
std::unique_ptr<sinsp_cycledumper> m_dumper;
event_filter_t m_filter;
captured_event_callback_t m_captured_event_callback;
before_open_t m_before_open;
before_capture_t m_before_capture;
after_capture_t m_after_capture;
callback_param m_param{};
Expand Down
Loading
Loading