Skip to content

Commit

Permalink
Reorganize win/linux + has_capture support
Browse files Browse the repository at this point in the history
Reorganize the way the async lookups are done to better support the
choices of different osen (linux, windows, and HAS_CAPTURE).

Make the previously statics related to curl lookups, docker paths,
etc. members of docker_async_source, now that it's a long-lived object
outside of any docker::resolve().

New methods init_docker_conn()/free_docker_conn() are OS-specific and
for linux handle initializing the m_curl handle and destroying it.

Move the inspector to the docker_async_source constructor as an
inspector exists at the time the docker_async_source is created.

Pass failures in docker_async_source back to the docker caller via a
m_successful. If not successful, future docker lookups are disabled.

Instead of compiling out the curl handles when HAS_CAPTURE is false,
always compile the code for them but don't even attempt any lookups
in docker::resolve. Note that docker_async_source::get_docker no longer
changes behavior based on HAS_CAPTURE.
  • Loading branch information
mstemm committed Mar 8, 2019
1 parent f869a04 commit 09ca9ec
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 75 deletions.
26 changes: 20 additions & 6 deletions userspace/libsinsp/container_engine/docker.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ class sinsp_threadinfo;
namespace libsinsp {
namespace container_engine {

class docker_async_source : public sysdig::async_key_value_source<std::string, sinsp_container_info>
struct container_lookup_result
{
bool m_successful;
sinsp_container_info m_container_info;
};

class docker_async_source : public sysdig::async_key_value_source<std::string, container_lookup_result>
{
enum docker_response
{
Expand All @@ -51,10 +57,9 @@ class docker_async_source : public sysdig::async_key_value_source<std::string, s
};

public:
docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms);
docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, sinsp *inspector);
virtual ~docker_async_source();

void set_inspector(sinsp *inspector);
static void set_query_image_info(bool query_image_info);

// Note that this tid is the current top tid for this container
Expand All @@ -72,14 +77,24 @@ class docker_async_source : public sysdig::async_key_value_source<std::string, s
protected:
void run_impl();

// These 4 methods are OS-dependent and defined in docker_{linux,win}.cpp
void init_docker_conn();
void free_docker_conn();
std::string build_request(const std::string& url);

docker_response get_docker(const std::string& url, std::string &json);

bool parse_docker(std::string &container_id, sinsp_container_info *container);

static std::string m_api_version;
sinsp *m_inspector;

std::string m_docker_unix_socket_path;
std::string m_api_version;

#ifndef _WIN32
CURLM *m_curlm;
CURL *m_curl;
#endif

static bool m_query_image_info;

// Maps from container id to the "top" threadinfo in the
Expand All @@ -101,7 +116,6 @@ class docker
bool resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info);
static void cleanup();
static void parse_json_mounts(const Json::Value &mnt_obj, std::vector<sinsp_container_info::container_mount_info> &mounts);
static void set_enabled(bool enabled);

// Container name only set for windows. For linux name must be fetched via lookup
static bool detect_docker(const sinsp_threadinfo* tinfo, std::string& container_id, std::string &container_name);
Expand Down
63 changes: 37 additions & 26 deletions userspace/libsinsp/container_engine/docker_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,22 @@ limitations under the License.

using namespace libsinsp::container_engine;

docker_async_source::docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms)
: async_key_value_source(max_wait_ms, ttl_ms)
docker_async_source::docker_async_source(uint64_t max_wait_ms, uint64_t ttl_ms, sinsp *inspector)
: async_key_value_source(max_wait_ms, ttl_ms),
m_inspector(inspector),
m_docker_unix_socket_path("/var/run/docker.sock"),
#ifdef _WIN32
m_api_version("/v1.30"),
#else
m_api_version("/v1.24")
#endif
{
init_docker_conn();
}

docker_async_source::~docker_async_source()
{
}

void docker_async_source::set_inspector(sinsp *inspector)
{
m_inspector = inspector;
free_docker_conn();
}

void docker_async_source::run_impl()
Expand All @@ -45,19 +49,22 @@ void docker_async_source::run_impl()

while (dequeue_next_key(container_id))
{
sinsp_container_info container;
container.m_type = CT_DOCKER;
container.m_id = container_id;
container_lookup_result res;

if(!parse_docker(container_id, &container))
res.m_successful = true;
res.m_container_info.m_type = CT_DOCKER;
res.m_container_info.m_id = container_id;

if(!parse_docker(container_id, &res.m_container_info))
{
g_logger.format(sinsp_logger::SEV_DEBUG, "Failed to get Docker metadata for container %s",
container_id.c_str());
res.m_successful = false;
}

// Return a container_info object either way, to
// ensure any new container callbacks are called.
store_value(container_id, container);
// Return a result object either way, to ensure any
// new container callbacks are called.
store_value(container_id, res);
}
}

Expand Down Expand Up @@ -157,11 +164,6 @@ void docker_async_source::set_query_image_info(bool query_image_info)
m_query_image_info = query_image_info;
}

void docker::set_enabled(bool enabled)
{
m_enabled = enabled;
}

bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info)
{
std::string container_id, container_name;
Expand All @@ -175,9 +177,8 @@ bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo,
if(!g_docker_info_source)
{
uint64_t max_wait_ms = 10000;
docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms);
docker_async_source *src = new docker_async_source(docker_async_source::NO_WAIT_LOOKUP, max_wait_ms, manager->get_inspector());
g_docker_info_source.reset(src);
g_docker_info_source->set_inspector(manager->get_inspector());
}

if(!detect_docker(tinfo, container_id, container_name))
Expand Down Expand Up @@ -208,6 +209,7 @@ bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo,
existing_container_info = manager->get_container(container_id);
}

#ifdef HAS_CAPTURE
// Possibly start a lookup for this container info
if(!existing_container_info->m_metadata_complete &&
query_os_for_missing_info)
Expand All @@ -224,6 +226,7 @@ bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo,
g_docker_info_source->update_top_tid(container_id, tinfo);
}
}
#endif

// Returning true will prevent other container engines from
// trying to resolve the container, so only return true if we
Expand All @@ -233,14 +236,22 @@ bool docker::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo,

void docker::parse_docker_async(sinsp *inspector, std::string &container_id, sinsp_container_manager *manager)
{
auto cb = [manager](const std::string &container_id, const sinsp_container_info &container_info)
auto cb = [manager](const std::string &container_id, const container_lookup_result &res)
{
int64_t top_tid = docker::g_docker_info_source->get_top_tid(container_id);
// If here, we know it's a docker container, so set the type
manager->notify_new_container(container_info, top_tid);
if(!res.m_successful)
{
// Disable further lookups
m_enabled = false;
}
else
{
int64_t top_tid = docker::g_docker_info_source->get_top_tid(container_id);
// If here, we know it's a docker container, so set the type
manager->notify_new_container(res.m_container_info, top_tid);
}
};

sinsp_container_info dummy;
container_lookup_result dummy;

if (g_docker_info_source->lookup(container_id, dummy, cb))
{
Expand Down
73 changes: 32 additions & 41 deletions userspace/libsinsp/container_engine/docker_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,13 @@ using namespace libsinsp::container_engine;
using namespace libsinsp::runc;

namespace {
std::string s_docker_unix_socket_path = "/var/run/docker.sock";
#if defined(HAS_CAPTURE)
CURLM *s_curlm = NULL;
CURL *s_curl = NULL;

size_t docker_curl_write_callback(const char* ptr, size_t size, size_t nmemb, string* json)
{
const std::size_t total = size * nmemb;
json->append(ptr, total);
return total;
}
#endif

constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = {
{"/", ""}, // non-systemd docker
Expand All @@ -48,46 +43,47 @@ constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = {
};
}

std::string docker_async_source::m_api_version = "/v1.24";
bool docker::m_enabled = true;
std::unique_ptr<docker_async_source> docker::g_docker_info_source;

docker::docker()
{
#if defined(HAS_CAPTURE)
if(!s_curlm)
}

void docker::cleanup()
{
g_docker_info_source.reset(NULL);
}

void docker_async_source::init_docker_conn()
{
if(!m_curlm)
{
s_curl = curl_easy_init();
s_curlm = curl_multi_init();
m_curl = curl_easy_init();
m_curlm = curl_multi_init();

if(s_curlm)
if(m_curlm)
{
curl_multi_setopt(s_curlm, CURLMOPT_PIPELINING, CURLPIPE_HTTP1|CURLPIPE_MULTIPLEX);
curl_multi_setopt(m_curlm, CURLMOPT_PIPELINING, CURLPIPE_HTTP1|CURLPIPE_MULTIPLEX);
}

if(s_curl)
if(m_curl)
{
auto docker_path = scap_get_host_root() + s_docker_unix_socket_path;
curl_easy_setopt(s_curl, CURLOPT_UNIX_SOCKET_PATH, docker_path.c_str());
curl_easy_setopt(s_curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(s_curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(s_curl, CURLOPT_WRITEFUNCTION, docker_curl_write_callback);
auto docker_path = scap_get_host_root() + m_docker_unix_socket_path;
curl_easy_setopt(m_curl, CURLOPT_UNIX_SOCKET_PATH, docker_path.c_str());
curl_easy_setopt(m_curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, docker_curl_write_callback);
}
}
#endif
}

void docker::cleanup()
void docker_async_source::free_docker_conn()
{
#if defined(HAS_CAPTURE)
curl_easy_cleanup(s_curl);
s_curl = NULL;
curl_multi_cleanup(s_curlm);
s_curlm = NULL;

g_docker_info_source.reset(NULL);
m_enabled = true;
#endif
curl_easy_cleanup(m_curl);
m_curl = NULL;
curl_multi_cleanup(m_curlm);
m_curlm = NULL;
}

std::string docker_async_source::build_request(const std::string &url)
Expand All @@ -97,19 +93,18 @@ std::string docker_async_source::build_request(const std::string &url)

docker_async_source::docker_response docker_async_source::get_docker(const std::string& url, std::string &json)
{
#ifdef HAS_CAPTURE
if(curl_easy_setopt(s_curl, CURLOPT_URL, url.c_str()) != CURLE_OK)
if(curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()) != CURLE_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
}
if(curl_easy_setopt(s_curl, CURLOPT_WRITEDATA, &json) != CURLE_OK)
if(curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &json) != CURLE_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
}

if(curl_multi_add_handle(s_curlm, s_curl) != CURLM_OK)
if(curl_multi_add_handle(m_curlm, m_curl) != CURLM_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
Expand All @@ -118,7 +113,7 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
while(true)
{
int still_running;
CURLMcode res = curl_multi_perform(s_curlm, &still_running);
CURLMcode res = curl_multi_perform(m_curlm, &still_running);
if(res != CURLM_OK)
{
ASSERT(false);
Expand All @@ -131,22 +126,22 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
}

int numfds;
res = curl_multi_wait(s_curlm, NULL, 0, -1, &numfds);
res = curl_multi_wait(m_curlm, NULL, 0, -1, &numfds);
if(res != CURLM_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
}
}

if(curl_multi_remove_handle(s_curlm, s_curl) != CURLM_OK)
if(curl_multi_remove_handle(m_curlm, m_curl) != CURLM_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
}

long http_code = 0;
if(curl_easy_getinfo(s_curl, CURLINFO_RESPONSE_CODE, &http_code) != CURLE_OK)
if(curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &http_code) != CURLE_OK)
{
ASSERT(false);
return docker_response::RESP_ERROR;
Expand All @@ -155,7 +150,6 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
{
case 0: /* connection failed, apparently */
g_logger.format(sinsp_logger::SEV_NOTICE, "Docker connection failed, disabling Docker container engine");
docker::set_enabled(false);
return docker_response::RESP_ERROR;
case 200:
return docker_response::RESP_OK;
Expand All @@ -164,9 +158,6 @@ docker_async_source::docker_response docker_async_source::get_docker(const std::
}

return docker_response::RESP_OK;
#else
return docker_response::RESP_ERROR;
#endif
}

bool docker::detect_docker(const sinsp_threadinfo *tinfo, std::string &container_id, std::string &container_name)
Expand Down
14 changes: 12 additions & 2 deletions userspace/libsinsp/container_engine/docker_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ limitations under the License.

using namespace libsinsp::container_engine;

std::string docker::m_api_version = "/v1.30";

docker::docker()
{
}

void docker::cleanup()
{
// Can only be set to false from failed lookups
m_enabled = true;

g_docker_info_source.reset(NULL);
}

void docker_async_source::init_docker_conn()
{
}

void docker_async_source::free_docker_conn()
{
}

Expand Down

0 comments on commit 09ca9ec

Please sign in to comment.