From 09ca9ec6cdee130905a6e8ba2db80e32a5b34428 Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Fri, 8 Mar 2019 15:12:59 -0800 Subject: [PATCH] Reorganize win/linux + has_capture support 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. --- userspace/libsinsp/container_engine/docker.h | 26 +++++-- .../container_engine/docker_common.cpp | 63 +++++++++------- .../container_engine/docker_linux.cpp | 73 ++++++++----------- .../libsinsp/container_engine/docker_win.cpp | 14 +++- 4 files changed, 101 insertions(+), 75 deletions(-) diff --git a/userspace/libsinsp/container_engine/docker.h b/userspace/libsinsp/container_engine/docker.h index 5b14a245f6..eabfbce66d 100644 --- a/userspace/libsinsp/container_engine/docker.h +++ b/userspace/libsinsp/container_engine/docker.h @@ -41,7 +41,13 @@ class sinsp_threadinfo; namespace libsinsp { namespace container_engine { -class docker_async_source : public sysdig::async_key_value_source +struct container_lookup_result +{ + bool m_successful; + sinsp_container_info m_container_info; +}; + +class docker_async_source : public sysdig::async_key_value_source { enum docker_response { @@ -51,10 +57,9 @@ class docker_async_source : public sysdig::async_key_value_source &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); diff --git a/userspace/libsinsp/container_engine/docker_common.cpp b/userspace/libsinsp/container_engine/docker_common.cpp index 234f74f108..88c1bf26b9 100644 --- a/userspace/libsinsp/container_engine/docker_common.cpp +++ b/userspace/libsinsp/container_engine/docker_common.cpp @@ -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() @@ -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); } } @@ -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; @@ -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)) @@ -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) @@ -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 @@ -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)) { diff --git a/userspace/libsinsp/container_engine/docker_linux.cpp b/userspace/libsinsp/container_engine/docker_linux.cpp index c2e278d877..8e848f97eb 100644 --- a/userspace/libsinsp/container_engine/docker_linux.cpp +++ b/userspace/libsinsp/container_engine/docker_linux.cpp @@ -28,10 +28,6 @@ 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) { @@ -39,7 +35,6 @@ size_t docker_curl_write_callback(const char* ptr, size_t size, size_t nmemb, st json->append(ptr, total); return total; } -#endif constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = { {"/", ""}, // non-systemd docker @@ -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::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) @@ -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; @@ -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); @@ -131,7 +126,7 @@ 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); @@ -139,14 +134,14 @@ docker_async_source::docker_response docker_async_source::get_docker(const std:: } } - 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; @@ -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; @@ -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) diff --git a/userspace/libsinsp/container_engine/docker_win.cpp b/userspace/libsinsp/container_engine/docker_win.cpp index b3621759a1..054d7a2758 100644 --- a/userspace/libsinsp/container_engine/docker_win.cpp +++ b/userspace/libsinsp/container_engine/docker_win.cpp @@ -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() { }