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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Care to expand on this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until now the container.id field was just the truncated version of the container.full_id which was a 64 char string. This is not always the case (e.g. containerd permits to define an arbitrary container id string) and we should accept container id of arbitrary length. Given that, the container id can now be less than 12 chars.

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();
}
7 changes: 5 additions & 2 deletions test/libsinsp_e2e/event_capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ 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,
before_open_t before_open,
before_capture_t before_capture,
after_capture_t before_close,
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_before_open = std::move(before_open);
m_before_capture = std::move(before_capture);
m_after_capture = std::move(before_close);
m_filter = std::move(filter);

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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you need to introduce these new callback?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if it needs to stay, at the very least i'd rename before_close to after_capture too.

Copy link
Contributor Author

@therealbobo therealbobo Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The container engines are initialised before the start of the capture and so we need an hook to set the cri settings before the start of the capture and after the open.

Also, if it needs to stay, at the very least i'd rename before_close to after_capture too.

👍

open_engine(event_capture::get_engine(), sc_set);

const ::testing::TestInfo* const test_info =
Expand Down
14 changes: 11 additions & 3 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,7 +64,8 @@ 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,
before_open_t before_open,
before_capture_t before_capture,
after_capture_t before_close,
event_filter_t filter,
uint32_t max_thread_table_size,
Expand All @@ -87,7 +90,8 @@ class event_capture {
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,
before_open_t before_open = event_capture::do_nothing,
before_capture_t before_capture = event_capture::do_nothing,
after_capture_t before_close = event_capture::do_nothing,
libsinsp::events::set<ppm_sc_code> sc_set = {},
uint32_t max_thread_table_size = 131072,
Expand All @@ -96,6 +100,7 @@ class event_capture {
bool dump = true) {
event_capture capturing(std::move(captured_event_callback),
std::move(before_open),
std::move(before_capture),
std::move(before_close),
std::move(filter),
max_thread_table_size,
Expand Down Expand Up @@ -125,7 +130,8 @@ class event_capture {
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,
before_open_t before_open = event_capture::do_nothing,
before_capture_t before_capture = event_capture::do_nothing,
after_capture_t before_close = event_capture::do_nothing,
libsinsp::events::set<ppm_sc_code> sc_set = {},
uint32_t max_thread_table_size = 131072,
Expand All @@ -134,6 +140,7 @@ class event_capture {
bool dump = true) {
event_capture capturing(std::move(captured_event_callback),
std::move(before_open),
std::move(before_capture),
std::move(before_close),
std::move(filter),
max_thread_table_size,
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