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

CRI-O container support #1310

Merged
merged 5 commits into from
Feb 28, 2019
Merged

CRI-O container support #1310

merged 5 commits into from
Feb 28, 2019

Conversation

gnosek
Copy link
Contributor

@gnosek gnosek commented Feb 5, 2019

CRI-O uses CRI so most of the work is done already but it differs in some important ways from containerd and has to be supported explicitly.

Copy link
Contributor

@speedyguy17 speedyguy17 left a comment

Choose a reason for hiding this comment

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

looks mostly okay, some concerns about the string matching function though

namespace libsinsp {
namespace runc {

/// runc-based runtimes (Docker, containerd, CRI-O, probably others) use the same two cgroup layouts
Copy link
Contributor

Choose a reason for hiding this comment

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

i appreciate the comment, but still slightly confused. maybe just an exapmle for each of the runc runtimes we support?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you still find it confusing after looking at the instances of this struct (container_engine/cri.cpp, container_engine/docker_linux.cpp)?

Copy link
Contributor

Choose a reason for hiding this comment

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

less so, but one don't want to have to dig into the implementation to understand the api.

namespace {

const size_t CONTAINER_ID_LENGTH = 64;
const size_t REPORTED_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.

maybe a note what these values represent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what to add that wouldn't duplicate the constant names or the static_assert below.

Copy link
Contributor

Choose a reason for hiding this comment

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

what does "reported" mean here? is it what's reported to us? what we're reporting to someone else?


static_assert(REPORTED_CONTAINER_ID_LENGTH <= CONTAINER_ID_LENGTH, "Reported container ID length cannot be longer than actual length");

bool detect_one_container_id(const std::string& cgroup, const std::string& prefix, const std::string& suffix, std::string& container_id)
Copy link
Contributor

Choose a reason for hiding this comment

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

should these two functions be static?

Copy link
Contributor

Choose a reason for hiding this comment

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

also quick comment on which are inputs and ouputs of this function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They're already in a private namespace.

I'll add a comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

must have missed that they're still in the NS. that's fine then.

const char* suffix;
};

/// If any of the cgroups of the thread in `tinfo` matches the `layout`, set `container_id` to the found id
Copy link
Contributor

Choose a reason for hiding this comment

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

what if multiple cgroups match the layout?

I assume we're assuming that impossible, and if that's the case ,we should perhaps note the assumption

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then we take the first matching one (AFAICT, the iteration order matches contents of /proc//cgroups). If the cgroups are inconsistent (matching different containers), there's no single right answer we can choose. What do you propose?

Copy link
Contributor

Choose a reason for hiding this comment

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

so long as we're okay with that, it's fine, but we should at least be explicit in that "if it matches multiple, we'll only return the first" or something


bool detect_container_id(const std::string& cgroup, const libsinsp::runc::cgroup_layout *layout, std::string &container_id)
{
for(size_t i = 0; layout[i].prefix && layout[i].suffix; ++i)
Copy link
Contributor

Choose a reason for hiding this comment

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

this way of doing bounds checking is somewhat risky, as it depends on someone putting null pointers at the end of arrays put in.

We should be able to statically determine the size of those arrays with sizeof(X)/sizeof(X[0]) and then that value can be passed in, or even better, we can wrap the array in a struct which stores the size of the array, calculated at compile time

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sizeof won't work across function calls. Given that we have only two of these arrays, hardcoded in the source and not modifiable at runtime, I'm not sure it's a big deal (if you insist, I'd probably go for std::array).

Copy link
Contributor

Choose a reason for hiding this comment

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

i don't think the number of instance is the issue as much as the fact that depending on the last entry of an array to be null in order to prevent a segfault is not really idiomatic, and there's no compelling reason to do it that way when safer options exist.

I didn't mean computing sizeof in the function itself, but along with the array
static const int foo = {1,5,7,2,7};
static const foo_length = sizeof(foo) / sizeof(foo[0]);

Then you use foo_length as your loop bounds, as it's clear and safe.

std::array is fine or even vector would work fine here as well

userspace/libsinsp/runc.cpp Outdated Show resolved Hide resolved

static_assert(std::string::npos == (size_t)(-1), "std::string::npos must be largest valid size_t");
size_t invalid_ch_pos = cgroup.find_first_not_of(CONTAINER_ID_VALID_CHARACTERS, pos);
if (invalid_ch_pos < CONTAINER_ID_LENGTH)
Copy link
Contributor

Choose a reason for hiding this comment

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

just match the ID against [0-9a-fA-F]{64} ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is literally what this code does. Just not using regexes.

#include "sinsp.h"
#include "sinsp_int.h"

namespace {
bool pod_uses_host_netns(const runtime::v1alpha2::PodSandboxStatusResponse& resp)
Copy link
Contributor

Choose a reason for hiding this comment

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

is this common in the code base instead of static?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I got tired of your (plural you) review comments asking me to switch from static to namespace{}. Make up your mind, will you? :P

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah i don't really care either way, i was just asking to ensure it's consistent

userspace/libsinsp/cri.cpp Show resolved Hide resolved
userspace/libsinsp/container_info.h Show resolved Hide resolved

constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = {
{"/", ""}, // non-systemd docker
{"/docker-", ".scope"}, // systemd docker
Copy link
Contributor

Choose a reason for hiding this comment

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

How confident are we about the ".scope" suffix of systemd.
this page here:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/resource_management_guide/sec-default_cgroup_hierarchies
seems to suggest we could have ".slice" too.

Will review the entire PR a little later. Wanted to capture this one thing first.

bool cri::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info)
{
sinsp_container_info container_info;

if(docker::detect_docker(tinfo, container_info.m_id))
if(matches_runc_cgroups(tinfo, CRI_CGROUP_LAYOUT, container_info.m_id))
{
container_info.m_type = s_cri_runtime_type;
Copy link
Contributor

@krishnan-ramkumar krishnan-ramkumar Feb 14, 2019

Choose a reason for hiding this comment

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

Is the need here only to specify if it is a CRI-based run-time or not ? Do we not need to know if it is CT_CRIO or CT_CONTAINERD ?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, we must be as specific as possible. i think we should report a container type being "CRI" only as a last resort.

@@ -79,32 +109,27 @@ bool parse_cri(sinsp_container_manager *manager, sinsp_container_info *container
parse_cri_image(resp_container, container);
parse_cri_mounts(resp_container, container);

const auto &info_it = resp.info().find("info");
if(info_it == resp.info().end())
if(parse_containerd(resp, container, tinfo))
Copy link
Contributor

Choose a reason for hiding this comment

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

this call seems out of place. why call logic specific to containerd unconditionally when the container type is known at this point. if there is an expectation that the logic in parse_containerd() could apply to cri-o as well, why not rename the function to be clear?

Copy link
Contributor

@anoop-sysd anoop-sysd Feb 14, 2019

Choose a reason for hiding this comment

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

btw, can't line 102 be removed since the same operation is done in the caller of this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

About the duplicate assignment, good catch, removed.

parse_containerd should be pretty cheap when the engine is not containerd compatible (no metadata in info) and if some other runtime decides that this is actually worth following, it saves us two extra api calls.

I'd leave this as is (currently it only applies to containerd and I don't have a better idea for the name).

bool cri::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info)
{
sinsp_container_info container_info;

if(docker::detect_docker(tinfo, container_info.m_id))
if(matches_runc_cgroups(tinfo, CRI_CGROUP_LAYOUT, container_info.m_id))
{
container_info.m_type = s_cri_runtime_type;
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, we must be as specific as possible. i think we should report a container type being "CRI" only as a last resort.

@@ -6151,6 +6151,9 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len
case sinsp_container_type::CT_CONTAINERD:
m_tstr = "containerd";
break;
case sinsp_container_type::CT_CRIO:
m_tstr = "crio";
Copy link
Contributor

Choose a reason for hiding this comment

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

"cri-o"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done (in both places). AFAIK this doesn't relate to the backend at all, just to container.type=cri-o style filters and Lua chisels

@@ -1193,6 +1193,10 @@ int lua_cbacks::get_container_table(lua_State *ls)
{
lua_pushstring(ls, "containerd");
}
else if(it->second.m_type == CT_CRIO)
{
lua_pushstring(ls, "crio");
Copy link
Contributor

Choose a reason for hiding this comment

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

"cri-o"

@anoop-sysd
Copy link
Contributor

@gnosek , i just saw your comment on how the container type is stringified in the backend. so if "cri-o" is not possible, then ignore my comments about renaming "crio".

While containerd returns everything in the `info` dictionary,
it's empty for CRIO, which means we need to get the IP address
and the image ID from extra API calls.

Unfortunately, it seems there's no way to get:
* environment variables
* privileged status
* resource limits
in any way from CRIO.
We set the container type in the calling method
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants