Skip to content

Commit 3c6b970

Browse files
committed
feat(libsinsp/container_engine/containerd): support image digest retrival
Signed-off-by: Roberto Scolaro <roberto.scolaro21@gmail.com>
1 parent ca6858f commit 3c6b970

File tree

6 files changed

+310
-36
lines changed

6 files changed

+310
-36
lines changed

userspace/libsinsp/CMakeLists.txt

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,17 +257,45 @@ function(prepare_containerd_grpc)
257257
${CMAKE_CURRENT_SOURCE_DIR}/container_engine/containerd/containers.proto
258258
${DEST}/containers.proto COPYONLY
259259
)
260+
configure_file(
261+
${CMAKE_CURRENT_SOURCE_DIR}/container_engine/containerd/images.proto ${DEST}/images.proto
262+
COPYONLY
263+
)
264+
configure_file(
265+
${CMAKE_CURRENT_SOURCE_DIR}/container_engine/containerd/descriptor.proto
266+
${DEST}/descriptor.proto COPYONLY
267+
)
260268
add_custom_command(
261-
OUTPUT ${DEST}/containers.grpc.pb.cc ${DEST}/containers.grpc.pb.h ${DEST}/containers.pb.cc
269+
OUTPUT ${DEST}/containers.grpc.pb.cc
270+
${DEST}/containers.grpc.pb.h
271+
${DEST}/containers.pb.cc
262272
${DEST}/containers.pb.h
273+
${DEST}/images.grpc.pb.cc
274+
${DEST}/images.grpc.pb.h
275+
${DEST}/images.pb.cc
276+
${DEST}/images.pb.h
277+
${DEST}/descriptor.grpc.pb.cc
278+
${DEST}/descriptor.grpc.pb.h
279+
${DEST}/descriptor.pb.cc
280+
${DEST}/descriptor.pb.h
263281
COMMENT "Generate containerd grpc code"
264282
DEPENDS
265283
COMMAND ${PROTOC} -I ${DEST} --cpp_out=${DEST} ${DEST}/containers.proto
266284
COMMAND ${PROTOC} -I ${DEST} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
267285
${DEST}/containers.proto
286+
COMMAND ${PROTOC} -I ${DEST} --cpp_out=${DEST} ${DEST}/descriptor.proto
287+
COMMAND ${PROTOC} -I ${DEST} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
288+
${DEST}/descriptor.proto
289+
COMMAND ${PROTOC} -I ${DEST} --cpp_out=${DEST} ${DEST}/images.proto
290+
COMMAND ${PROTOC} -I ${DEST} --grpc_out=. --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}
291+
${DEST}/images.proto
268292
WORKING_DIRECTORY ${DEST}
269293
)
270-
add_library(containerd_interface STATIC ${DEST}/containers.pb.cc ${DEST}/containers.grpc.pb.cc)
294+
add_library(
295+
containerd_interface STATIC
296+
${DEST}/containers.pb.cc ${DEST}/containers.grpc.pb.cc ${DEST}/descriptor.pb.cc
297+
${DEST}/descriptor.grpc.pb.cc ${DEST}/images.pb.cc ${DEST}/images.grpc.pb.cc
298+
)
271299
target_include_directories(containerd_interface PUBLIC $<BUILD_INTERFACE:${DEST}>)
272300
target_link_libraries(
273301
containerd_interface
@@ -281,7 +309,8 @@ function(prepare_containerd_grpc)
281309
)
282310
add_dependencies(containerd_interface grpc)
283311
install(
284-
FILES ${DEST}/containers.grpc.pb.h ${DEST}/containers.pb.h
312+
FILES ${DEST}/containers.grpc.pb.h ${DEST}/containers.pb.h ${DEST}/descriptor.grpc.pb.h
313+
${DEST}/descriptor.pb.h ${DEST}/images.grpc.pb.h ${DEST}/images.pb.h
285314
DESTINATION
286315
"${CMAKE_INSTALL_INCLUDEDIR}/${LIBS_PACKAGE_NAME}/libsinsp/container_engine/containerd"
287316
COMPONENT "scap"
@@ -318,7 +347,7 @@ if(NOT WIN32)
318347
prepare_containerd_grpc()
319348

320349
target_link_libraries(sinsp PUBLIC cri_v1alpha2 cri_v1)
321-
target_link_libraries(sinsp PUBLIC containerd_interface)
350+
target_link_libraries(sinsp PRIVATE containerd_interface)
322351

323352
if(NOT MUSL_OPTIMIZED_BUILD)
324353
find_library(LIB_ANL anl)

userspace/libsinsp/container_engine/containerd.cpp

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,19 @@ constexpr const std::string_view CONTAINERD_SOCKETS[] = {
3434
};
3535

3636
bool containerd_async_source::is_ok() {
37-
return m_stub != nullptr;
37+
return m_container_stub != nullptr && m_image_stub != nullptr;
38+
}
39+
40+
static inline void setup_grpc_client_context(grpc::ClientContext &context) {
41+
auto deadline = std::chrono::system_clock::now() +
42+
std::chrono::milliseconds(libsinsp::cri::cri_settings::get_cri_timeout());
43+
44+
context.set_deadline(deadline);
45+
46+
// The `default` namesapce is the default one of containerd
47+
// and the one used by host-containers in bottlerocket.
48+
// This is mandatory to query the containers.
49+
context.AddMetadata("containerd-namespace", "default");
3850
}
3951

4052
containerd_async_source::containerd_async_source(const std::string &socket_path,
@@ -47,30 +59,50 @@ containerd_async_source::containerd_async_source(const std::string &socket_path,
4759
std::shared_ptr<grpc::Channel> channel =
4860
libsinsp::grpc_channel_registry::get_channel("unix://" + socket_path, &args);
4961

50-
m_stub = ContainerdService::Containers::NewStub(channel);
62+
// Check the status of the container stub.
63+
{
64+
grpc::ClientContext context;
65+
setup_grpc_client_context(context);
5166

52-
ContainerdService::ListContainersRequest req;
53-
ContainerdService::ListContainersResponse resp;
67+
m_container_stub = ContainerdContainerService::Containers::NewStub(channel);
5468

55-
grpc::ClientContext context;
56-
auto deadline = std::chrono::system_clock::now() +
57-
std::chrono::milliseconds(libsinsp::cri::cri_settings::get_cri_timeout());
58-
context.set_deadline(deadline);
69+
ContainerdContainerService::ListContainersRequest req;
70+
ContainerdContainerService::ListContainersResponse resp;
5971

60-
// The `default` namesapce is the default one of containerd
61-
// and the one used by host-containers in bottlerocket.
62-
// This is mandatory to query the containers.
63-
context.AddMetadata("containerd-namespace", "default");
64-
grpc::Status status = m_stub->List(&context, req, &resp);
72+
grpc::Status status = m_container_stub->List(&context, req, &resp);
6573

66-
if(!status.ok()) {
67-
libsinsp_logger()->format(sinsp_logger::SEV_NOTICE,
68-
"containerd (%s): containerd runtime returned an error after "
69-
"trying to list containerd: %s",
70-
socket_path.c_str(),
71-
status.error_message().c_str());
72-
m_stub.reset(nullptr);
73-
return;
74+
if(!status.ok()) {
75+
libsinsp_logger()->format(sinsp_logger::SEV_NOTICE,
76+
"containerd (%s): containerd runtime returned an error after "
77+
"trying to list containers: %s",
78+
socket_path.c_str(),
79+
status.error_message().c_str());
80+
m_container_stub.reset(nullptr);
81+
return;
82+
}
83+
}
84+
85+
// Check the status of the image stub.
86+
{
87+
grpc::ClientContext context;
88+
setup_grpc_client_context(context);
89+
90+
m_image_stub = ContainerdImageService::Images::NewStub(channel);
91+
92+
ContainerdImageService::ListImagesRequest req;
93+
ContainerdImageService::ListImagesResponse resp;
94+
95+
grpc::Status status = m_image_stub->List(&context, req, &resp);
96+
97+
if(!status.ok()) {
98+
libsinsp_logger()->format(sinsp_logger::SEV_NOTICE,
99+
"containerd (%s): containerd runtime returned an error after "
100+
"trying to list images: %s",
101+
socket_path.c_str(),
102+
status.error_message().c_str());
103+
m_image_stub.reset(nullptr);
104+
return;
105+
}
74106
}
75107
}
76108

@@ -81,8 +113,8 @@ containerd_async_source::~containerd_async_source() {
81113

82114
grpc::Status containerd_async_source::list_container_resp(
83115
const std::string &container_id,
84-
ContainerdService::ListContainersResponse &resp) {
85-
ContainerdService::ListContainersRequest req;
116+
ContainerdContainerService::ListContainersResponse &resp) {
117+
ContainerdContainerService::ListContainersRequest req;
86118

87119
// To match the container using a truncated containerd id
88120
// we need to use a match filter (~=).
@@ -92,7 +124,21 @@ grpc::Status containerd_async_source::list_container_resp(
92124
auto deadline = std::chrono::system_clock::now() +
93125
std::chrono::milliseconds(libsinsp::cri::cri_settings::get_cri_timeout());
94126
context.set_deadline(deadline);
95-
return m_stub->List(&context, req, &resp);
127+
return m_container_stub->List(&context, req, &resp);
128+
}
129+
130+
grpc::Status containerd_async_source::get_image_resp(
131+
const std::string &image_name,
132+
ContainerdImageService::GetImageResponse &resp) {
133+
ContainerdImageService::GetImageRequest req;
134+
135+
req.set_name(image_name);
136+
grpc::ClientContext context;
137+
context.AddMetadata("containerd-namespace", "default");
138+
auto deadline = std::chrono::system_clock::now() +
139+
std::chrono::milliseconds(libsinsp::cri::cri_settings::get_cri_timeout());
140+
context.set_deadline(deadline);
141+
return m_image_stub->Get(&context, req, &resp);
96142
}
97143

98144
libsinsp::container_engine::containerd::containerd(container_cache_interface &cache):
@@ -132,7 +178,7 @@ bool containerd_async_source::parse(const containerd_lookup_request &request,
132178

133179
// given the truncated container id, the full container id needs to be retrivied from
134180
// containerd.
135-
ContainerdService::ListContainersResponse resp;
181+
ContainerdContainerService::ListContainersResponse resp;
136182
grpc::Status status = list_container_resp(container_id, resp);
137183

138184
if(!status.ok()) {
@@ -174,9 +220,23 @@ bool containerd_async_source::parse(const containerd_lookup_request &request,
174220
// and the first part is the repo
175221
container.m_imagerepo = raw_image_splits[0].substr(0, raw_image_splits[0].rfind("/"));
176222
container.m_imagetag = raw_image_splits[1];
177-
container.m_imagedigest = "";
178223
container.m_type = CT_CONTAINERD;
179224

225+
// Retrieve the image digest.
226+
ContainerdImageService::GetImageResponse img_resp;
227+
status = get_image_resp(containers[0].image(), img_resp);
228+
229+
if(!status.ok()) {
230+
libsinsp_logger()->format(sinsp_logger::SEV_DEBUG,
231+
"containerd (%s): GetImageResponse status error message: (%s)",
232+
container.m_id.c_str(),
233+
status.error_message().c_str());
234+
235+
// Don't exit given that we have part of the needed information.
236+
}
237+
238+
container.m_imagedigest = img_resp.image().target().digest();
239+
180240
// Retrieve the labels.
181241
for(const auto &pair : containers[0].labels()) {
182242
if(pair.second.length() <= sinsp_container_info::m_container_label_max_length) {

userspace/libsinsp/container_engine/containerd.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ class sinsp_container_info;
2222
class sinsp_threadinfo;
2323

2424
#include <libsinsp/container_engine/containerd/containers.grpc.pb.h>
25+
#include <libsinsp/container_engine/containerd/images.grpc.pb.h>
2526
#include <libsinsp/container_engine/container_async_source.h>
2627
#include <libsinsp/container_engine/container_engine_base.h>
2728
#include <libsinsp/container_engine/sinsp_container_type.h>
2829

29-
namespace ContainerdService = containerd::services::containers::v1;
30+
namespace ContainerdContainerService = containerd::services::containers::v1;
31+
namespace ContainerdImageService = containerd::services::images::v1;
3032

3133
namespace libsinsp {
3234
namespace container_engine {
@@ -80,7 +82,6 @@ class containerd_async_source : public container_async_source<containerd_lookup_
8082
container_cache_interface* cache);
8183
virtual ~containerd_async_source();
8284

83-
// TODO probably remove
8485
bool is_ok();
8586

8687
private:
@@ -94,9 +95,13 @@ class containerd_async_source : public container_async_source<containerd_lookup_
9495
std::string container_id(const key_type& key) const override { return key.container_id; }
9596

9697
grpc::Status list_container_resp(const std::string& container_id,
97-
ContainerdService::ListContainersResponse& resp);
98+
ContainerdContainerService::ListContainersResponse& resp);
9899

99-
std::unique_ptr<ContainerdService::Containers::Stub> m_stub;
100+
grpc::Status get_image_resp(const std::string& image_name,
101+
ContainerdImageService::GetImageResponse& resp);
102+
103+
std::unique_ptr<ContainerdContainerService::Containers::Stub> m_container_stub;
104+
std::unique_ptr<ContainerdImageService::Images::Stub> m_image_stub;
100105
std::string m_socket_path;
101106
};
102107

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
syntax = "proto3";
18+
19+
package containerd.types;
20+
21+
option go_package = "github.com/containerd/containerd/api/types;types";
22+
23+
// Descriptor describes a blob in a content store.
24+
//
25+
// This descriptor can be used to reference content from an
26+
// oci descriptor found in a manifest.
27+
// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor
28+
message Descriptor {
29+
string media_type = 1;
30+
string digest = 2;
31+
int64 size = 3;
32+
map<string, string> annotations = 5;
33+
}

0 commit comments

Comments
 (0)