Skip to content

Commit

Permalink
Add an introspection endpoint for container metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
Molter73 committed Mar 26, 2024
1 parent b623e54 commit df4679f
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 17 deletions.
3 changes: 3 additions & 0 deletions collector/lib/CollectorConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ BoolEnvVar enable_runtime_filters("ROX_COLLECTOR_RUNTIME_FILTERS_ENABLED", false
BoolEnvVar use_docker_ce("ROX_COLLECTOR_CE_USE_DOCKER", false);
BoolEnvVar use_podman_ce("ROX_COLLECTOR_CE_USE_PODMAN", false);

BoolEnvVar enable_introspection("ROX_COLLECTOR_INTROSPECTION_ENABLE", false);

} // namespace

constexpr bool CollectorConfig::kTurnOffScrape;
Expand Down Expand Up @@ -79,6 +81,7 @@ void CollectorConfig::InitCollectorConfig(CollectorArgs* args) {
enable_runtime_filters_ = enable_runtime_filters.value();
use_docker_ce_ = use_docker_ce.value();
use_podman_ce_ = use_podman_ce.value();
enable_introspection_ = enable_introspection.value();

for (const auto& syscall : kSyscalls) {
syscalls_.push_back(syscall);
Expand Down
2 changes: 2 additions & 0 deletions collector/lib/CollectorConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class CollectorConfig {
bool EnableRuntimeFilters() const { return enable_runtime_filters_; }
bool UseDockerCe() const { return use_docker_ce_; }
bool UsePodmanCe() const { return use_podman_ce_; }
bool IsIntrospectionEnabled() const { return enable_introspection_; }
const std::vector<double>& GetConnectionStatsQuantiles() const { return connection_stats_quantiles_; }
double GetConnectionStatsError() const { return connection_stats_error_; }
unsigned int GetConnectionStatsWindow() const { return connection_stats_window_; }
Expand Down Expand Up @@ -113,6 +114,7 @@ class CollectorConfig {
bool enable_runtime_filters_;
bool use_docker_ce_;
bool use_podman_ce_;
bool enable_introspection_;
std::vector<double> connection_stats_quantiles_;
double connection_stats_error_;
unsigned int connection_stats_window_;
Expand Down
9 changes: 9 additions & 0 deletions collector/lib/CollectorService.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "CollectorService.h"

#include "ContainerInfoInspector.h"

extern "C" {
#include <signal.h>
}
Expand Down Expand Up @@ -58,6 +60,8 @@ void CollectorService::RunForever() {

std::unique_ptr<NetworkStatusNotifier> net_status_notifier;

std::unique_ptr<ContainerInfoInspector> container_info_inspector;

CLOG(INFO) << "Network scrape interval set to " << config_.ScrapeInterval() << " seconds";

if (config_.grpc_channel) {
Expand Down Expand Up @@ -99,6 +103,11 @@ void CollectorService::RunForever() {
CLOG(FATAL) << "Unable to start collector stats exporter";
}

if (config_.IsIntrospectionEnabled()) {
container_info_inspector = std::make_unique<ContainerInfoInspector>(system_inspector_.GetK8sInspector());
server.addHandler(container_info_inspector->kBaseRoute, container_info_inspector.get());
}

system_inspector_.Init(config_, conn_tracker);
system_inspector_.Start();

Expand Down
34 changes: 34 additions & 0 deletions collector/lib/ContainerInfoInspector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "ContainerInfoInspector.h"

#include <civetweb.h>
#include <string>
#include <string_view>

namespace collector {
const char* ContainerInfoInspector::kBaseRoute = "/state/containers/";

bool ContainerInfoInspector::handleGet(CivetServer* server, struct mg_connection* conn) {
const mg_request_info* req_info = mg_get_request_info(conn);
if (req_info == nullptr) {
return ServerError(conn, "unable to read request");
}

std::string_view url = req_info->local_uri;
std::string container_id(url.substr(url.rfind('/') + 1));

if (container_id.length() != 12) {
return ClientError(conn, "invalid container ID");
}

Json::Value root;

root["container_id"] = container_id;
root["namespace"] = std::string(k8s_inspector_->GetNamespace(container_id));

mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n");
mg_printf(conn, "%s\r\n", writer_.write(root).c_str());

return true;
}

} // namespace collector
34 changes: 34 additions & 0 deletions collector/lib/ContainerInfoInspector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef _CONTAINER_INFO_INSPECTOR_
#define _CONTAINER_INFO_INSPECTOR_

#include <CivetServer.h>
#include <civetweb.h>
#include <memory>
#include <string>
#include <unordered_map>

#include "IntrospectionEndpoint.h"
#include "K8s.h"
#include "json/writer.h"

namespace collector {

using QueryParams = std::unordered_map<std::string, std::string>;

class ContainerInfoInspector : public IntrospectionEndpoint {
public:
static const char* kBaseRoute;

ContainerInfoInspector(const std::shared_ptr<K8s>& k8s_inspector) : k8s_inspector_(k8s_inspector) {}

// implementation of CivetHandler
bool handleGet(CivetServer* server, struct mg_connection* conn) override;

private:
std::shared_ptr<K8s> k8s_inspector_;
Json::FastWriter writer_;
};

} // namespace collector

#endif //_CONTAINER_INFO_INSPECTOR_
33 changes: 33 additions & 0 deletions collector/lib/IntrospectionEndpoint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "IntrospectionEndpoint.h"

#include <sstream>

namespace collector {

QueryParams IntrospectionEndpoint::ParseParameters(const char* queryString) {
QueryParams params;

if (queryString == nullptr) {
return params;
}

std::stringstream query_stringstream(queryString);
while (query_stringstream.good()) {
std::string statement;

std::getline(query_stringstream, statement, '&');

size_t equal = statement.find('=');

if (equal != std::string::npos) {
params[statement.substr(0, equal)] = statement.substr(equal + 1);
}
}
return params;
}

std::optional<std::string> IntrospectionEndpoint::GetParameter(const QueryParams& params, const std::string& paramName) {
return params.count(paramName) != 0 ? std::make_optional(params.at(paramName)) : std::nullopt;
}

} // namespace collector
27 changes: 27 additions & 0 deletions collector/lib/IntrospectionEndpoint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef _INTROSPECTION_ENDPOINT_
#define _INTROSPECTION_ENDPOINT_

#include <CivetServer.h>
#include <optional>
#include <unordered_map>

namespace collector {

using QueryParams = std::unordered_map<std::string, std::string>;

class IntrospectionEndpoint : public CivetHandler {
protected:
static QueryParams ParseParameters(const char* queryString);
static std::optional<std::string> GetParameter(const QueryParams& params, const std::string& paramName);

static bool ServerError(struct mg_connection* conn, const char* err) {
return mg_send_http_error(conn, 500, err) >= 0;
}
static bool ClientError(struct mg_connection* conn, const char* err) {
return mg_send_http_error(conn, 400, err) >= 0;
}
};

} // namespace collector

#endif // _INTROSPECTION_ENDPOINT_
22 changes: 6 additions & 16 deletions collector/lib/K8s.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,22 @@ class K8s {
event_extractor_.Init(inspector);
}

inline std::string_view GetNamespace(sinsp_evt* event) {
inline std::string GetNamespace(sinsp_evt* event) {
const char* ns = event_extractor_.get_k8s_namespace(event);
return ns != nullptr ? ns : "";
}

inline std::string_view GetNamespace(const std::string& container_id) {
inline std::string GetNamespace(const std::string& container_id) {
return GetContainerLabel(container_id, "io.kubernetes.pod.namespace");
}

std::string GetContainerLabels(const std::string& container_id) {
const auto container = inspector_->m_container_manager.get_container(container_id);
if (container == nullptr) {
inline std::string GetContainerLabel(const std::string& container_id, const std::string& label) {
const auto& containers = *inspector_->m_container_manager.get_containers();
if (containers.count(container_id) == 0) {
return "";
}

std::stringstream ss;

for (const auto& [key, value] : container->m_labels) {
ss << key << ":" << value << ",";
}

return ss.str();
}

inline std::string_view GetContainerLabel(const std::string& container_id, const std::string& label) {
const auto container = inspector_->m_container_manager.get_container(container_id);
const auto& container = containers.at(container_id);
if (container == nullptr || container->m_labels.count(label) == 0) {
return "";
}
Expand Down
2 changes: 1 addition & 1 deletion collector/lib/ProcessSignalFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ ProcessSignal* ProcessSignalFormatter::CreateProcessSignal(sinsp_evt* event) {
}

CLOG(DEBUG) << "Process (" << signal->container_id() << ": " << signal->pid() << "): "
<< signal->name() << "[" << k8s_.GetNamespace(event) << "] "
<< signal->name() << " [" << k8s_.GetNamespace(event) << "]"
<< " (" << signal->exec_file_path() << ")"
<< " " << signal->args();

Expand Down
2 changes: 2 additions & 0 deletions collector/lib/system-inspector/Service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ bool Service::InitKernel(const CollectorConfig& config, const DriverCandidate& c
inspector_->get_parser()->set_track_connection_status(true);
}

k8s_inspector_.reset(new K8s(inspector_.get()));

if (config.EnableRuntimeFilters()) {
uint64_t mask = 1 << CT_CRI |
1 << CT_CRIO |
Expand Down
4 changes: 4 additions & 0 deletions collector/lib/system-inspector/Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "Control.h"
#include "DriverCandidates.h"
#include "K8s.h"
#include "SignalHandler.h"
#include "SignalServiceClient.h"
#include "SystemInspector.h"
Expand Down Expand Up @@ -44,6 +45,8 @@ class Service : public SystemInspector {

void GetProcessInformation(uint64_t pid, ProcessInfoCallbackRef callback);

std::shared_ptr<K8s> GetK8sInspector() { return k8s_inspector_; };

private:
FRIEND_TEST(SystemInspectorServiceTest, FilterEvent);

Expand All @@ -69,6 +72,7 @@ class Service : public SystemInspector {

mutable std::mutex libsinsp_mutex_;
std::unique_ptr<sinsp> inspector_;
std::shared_ptr<K8s> k8s_inspector_;
std::unique_ptr<sinsp_evt_formatter> default_formatter_;
std::unique_ptr<ISignalServiceClient> signal_client_;
std::vector<SignalHandlerEntry> signal_handlers_;
Expand Down

0 comments on commit df4679f

Please sign in to comment.