diff --git a/userspace/libsinsp/container.cpp b/userspace/libsinsp/container.cpp index 4c0b5d966e..b3cda3ab30 100644 --- a/userspace/libsinsp/container.cpp +++ b/userspace/libsinsp/container.cpp @@ -17,6 +17,8 @@ limitations under the License. */ +#include + #include "container_engine/cri.h" #include "container_engine/docker.h" #include "container_engine/rkt.h" @@ -128,8 +130,8 @@ bool sinsp_container_manager::resolve_container(sinsp_threadinfo* tinfo, bool qu #endif // CYGWING_AGENT - // Also identify if this thread is part of a container healthcheck - identify_healthcheck(tinfo); + // Also possibly set the category for the threadinfo + identify_category(tinfo); return matches; } @@ -165,9 +167,9 @@ string sinsp_container_manager::container_to_json(const sinsp_container_info& co container["Mounts"] = mounts; - if(!container_info.m_healthcheck_obj.isNull()) + for(auto &probe : container_info.m_health_probes) { - container["Healthcheck"] = container_info.m_healthcheck_obj; + probe.add_to_json(container); } char addrbuff[100]; @@ -282,15 +284,14 @@ string sinsp_container_manager::get_container_name(sinsp_threadinfo* tinfo) return res; } -void sinsp_container_manager::identify_healthcheck(sinsp_threadinfo *tinfo) +void sinsp_container_manager::identify_category(sinsp_threadinfo *tinfo) { - // This thread is a part of a container healthcheck if its - // parent thread is part of a health check. + // Categories are passed from parent to child threads sinsp_threadinfo* ptinfo = tinfo->get_parent_thread(); - if(ptinfo && ptinfo->m_is_container_healthcheck) + if(ptinfo && ptinfo->m_category != sinsp_threadinfo::CAT_NONE) { - tinfo->m_is_container_healthcheck = true; + tinfo->m_category = ptinfo->m_category; return; } @@ -301,17 +302,17 @@ void sinsp_container_manager::identify_healthcheck(sinsp_threadinfo *tinfo) return; } - // Otherwise, the thread is a part of a container healthcheck if: + // Otherwise, the thread is a part of a container health probe if: // - // 1. the comm and args match the container's healthcheck + // 1. the comm and args match one of the container's health probes // 2. we traverse the parent state and do *not* find vpid=1, // or find a process not in a container // - // This indicates the initial process of the healthcheck. + // This indicates the initial process of the health probe. + + sinsp_container_info::container_health_probe::probe_type ptype; - if(!cinfo->m_has_healthcheck || - cinfo->m_healthcheck_exe != tinfo->m_exe || - cinfo->m_healthcheck_args != tinfo->m_args) + if((ptype = cinfo->match_health_probe(tinfo)) == sinsp_container_info::container_health_probe::PT_NONE) { return; } @@ -339,7 +340,21 @@ void sinsp_container_manager::identify_healthcheck(sinsp_threadinfo *tinfo) if(!found_container_init) { - tinfo->m_is_container_healthcheck = true; + // Each health probe type maps to a command category + switch(ptype) + { + case sinsp_container_info::container_health_probe::PT_NONE: + break; + case sinsp_container_info::container_health_probe::PT_HEALTHCHECK: + tinfo->m_category = sinsp_threadinfo::CAT_HEALTHCHECK; + break; + case sinsp_container_info::container_health_probe::PT_LIVENESS_PROBE: + tinfo->m_category = sinsp_threadinfo::CAT_LIVENESS_PROBE; + break; + case sinsp_container_info::container_health_probe::PT_READINESS_PROBE: + tinfo->m_category = sinsp_threadinfo::CAT_READINESS_PROBE; + break; + } } } @@ -378,4 +393,4 @@ void sinsp_container_manager::set_cri_timeout(int64_t timeout_ms) #if defined(HAS_CAPTURE) libsinsp::container_engine::cri::set_cri_timeout(timeout_ms); #endif -} \ No newline at end of file +} diff --git a/userspace/libsinsp/container.h b/userspace/libsinsp/container.h index f741b2d0a7..2f091919ae 100644 --- a/userspace/libsinsp/container.h +++ b/userspace/libsinsp/container.h @@ -46,12 +46,11 @@ class sinsp_container_manager void dump_containers(scap_dumper_t* dumper); string get_container_name(sinsp_threadinfo* tinfo); - // Set tinfo's is_container_healthcheck attribute to true if - // it is identified as a container healthcheck. It will *not* - // set it to false by default, so a threadinfo that is - // initially identified as a health check will remain one + // Set tinfo's m_category based on the container context. It + // will *not* change any category to NONE, so a threadinfo + // that initially has a category will retain its category // across execs e.g. "sh -c /bin/true" execing /bin/true. - void identify_healthcheck(sinsp_threadinfo *tinfo); + void identify_category(sinsp_threadinfo *tinfo); bool container_exists(const string& container_id) const { return m_containers.find(container_id) != m_containers.end(); diff --git a/userspace/libsinsp/container_engine/docker_common.cpp b/userspace/libsinsp/container_engine/docker_common.cpp index 75a554ddb9..825abc278e 100644 --- a/userspace/libsinsp/container_engine/docker_common.cpp +++ b/userspace/libsinsp/container_engine/docker_common.cpp @@ -87,19 +87,17 @@ bool docker::parse_docker(sinsp_container_manager* manager, sinsp_container_info container->m_imageid = imgstr.substr(cpos + 1); } - container->parse_healthcheck(config_obj["Healthcheck"]); - - // Saving full healthcheck for container event parsing/writing - container->m_healthcheck_obj = config_obj["Healthcheck"]; + // Add any health checks described in the container config/labels. + sinsp_container_info::container_health_probe::add_health_probes(config_obj, container->m_health_probes); // containers can be spawned using just the imageID as image name, // with or without the hash prefix (e.g. sha256:) bool no_name = !container->m_imageid.empty() && - strncmp(container->m_image.c_str(), container->m_imageid.c_str(), - MIN(container->m_image.length(), container->m_imageid.length())) == 0; + strncmp(container->m_image.c_str(), container->m_imageid.c_str(), + MIN(container->m_image.length(), container->m_imageid.length())) == 0; no_name |= !imgstr.empty() && - strncmp(container->m_image.c_str(), imgstr.c_str(), - MIN(container->m_image.length(), imgstr.length())) == 0; + strncmp(container->m_image.c_str(), imgstr.c_str(), + MIN(container->m_image.length(), imgstr.length())) == 0; if(!no_name || !m_query_image_info) { @@ -288,3 +286,4 @@ bool docker::parse_docker(sinsp_container_manager* manager, sinsp_container_info #endif return true; } + diff --git a/userspace/libsinsp/container_info.cpp b/userspace/libsinsp/container_info.cpp index 61ccc667df..e6096c303b 100644 --- a/userspace/libsinsp/container_info.cpp +++ b/userspace/libsinsp/container_info.cpp @@ -17,49 +17,246 @@ limitations under the License. */ +#include + #include "container_info.h" #include "sinsp.h" #include "sinsp_int.h" -const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_idx(uint32_t idx) const +sinsp_container_info::container_health_probe::container_health_probe() { - if (idx >= m_mounts.size()) +} + +sinsp_container_info::container_health_probe::container_health_probe(const container_health_probe &other) + : m_probe_type(other.m_probe_type), + m_health_probe_exe(other.m_health_probe_exe), + m_health_probe_args(other.m_health_probe_args), + m_obj(other.m_obj) +{ +} + +sinsp_container_info::container_health_probe::container_health_probe(container_health_probe &&other) + : m_probe_type(other.m_probe_type), + m_health_probe_exe(std::move(other.m_health_probe_exe)), + m_health_probe_args(std::move(other.m_health_probe_args)), + m_obj(std::move(other.m_obj)) +{ +} + +sinsp_container_info::container_health_probe::~container_health_probe() +{ +} + +sinsp_container_info::container_health_probe &sinsp_container_info::container_health_probe::operator=(const container_health_probe &other) +{ + m_probe_type = other.m_probe_type; + m_health_probe_exe = other.m_health_probe_exe; + m_health_probe_args = other.m_health_probe_args; + m_obj = other.m_obj; + + return *this; +} + +void sinsp_container_info::container_health_probe::add_health_probes(const Json::Value &config_obj, + std::list &probes) +{ + Json::Value spec; + bool liveness_readiness_added = false; + + // When parsing the full container json for live containers, a label contains stringified json that + // contains the probes. + if (container_health_probe::get_k8s_pod_spec(config_obj, spec)) { - return NULL; + if(spec.isMember("livenessProbe")) + { + container_health_probe probe; + if(probe.parse_liveness_readiness_probe(spec["livenessProbe"], PT_LIVENESS_PROBE)) + { + probes.push_back(probe); + liveness_readiness_added = true; + } + } + else if(spec.isMember("readinessProbe")) + { + container_health_probe probe; + if(probe.parse_liveness_readiness_probe(spec["readinessProbe"], PT_READINESS_PROBE)) + { + probes.push_back(probe); + liveness_readiness_added = true; + } + } } - return &(m_mounts[idx]); + // When parsing the json for container_json events, + // the livenessProbe/readinessProbe are directly + // available. + if (config_obj.isMember("livenessProbe")) + { + container_health_probe probe; + if(probe.parse_liveness_readiness_probe(config_obj["livenessProbe"], PT_LIVENESS_PROBE)) + { + probes.push_back(probe); + liveness_readiness_added = true; + } + } + + if (config_obj.isMember("readinessProbe")) + { + container_health_probe probe; + if(probe.parse_liveness_readiness_probe(config_obj["readinessProbe"], PT_READINESS_PROBE)) + { + probes.push_back(probe); + liveness_readiness_added = true; + } + } + + // To avoid any confusion about containers that both refer to + // a healthcheck and liveness/readiness probe, we only + // consider a healthcheck if no liveness/readiness was added. + + if(!liveness_readiness_added && config_obj.isMember("Healthcheck")) + { + container_health_probe probe; + if(probe.parse_healthcheck(config_obj["Healthcheck"])) + { + probes.push_back(probe); + } + } } -const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_source(std::string &source) const +void sinsp_container_info::container_health_probe::add_to_json(Json::Value &container_obj) const { - // note: linear search - for (auto &mntinfo :m_mounts) + switch(m_probe_type) { - if(sinsp_utils::glob_match(source.c_str(), mntinfo.m_source.c_str())) + case PT_HEALTHCHECK: + container_obj["Healthcheck"] = m_obj; + break; + case PT_LIVENESS_PROBE: + container_obj["livenessProbe"] = m_obj; + break; + case PT_READINESS_PROBE: + container_obj["readinessProbe"] = m_obj; + break; + default: + g_logger.format(sinsp_logger::SEV_ERROR, "Unknown health probe type %d", + m_probe_type); + } +} + +bool sinsp_container_info::container_health_probe::get_k8s_pod_spec(const Json::Value &config_obj, Json::Value &spec) +{ + std::string cfg_str; + Json::Reader reader; + std::string k8s_label = "annotation.kubectl.kubernetes.io/last-applied-configuration"; + + if(config_obj.isNull() || + !config_obj.isMember("Labels") || + !config_obj["Labels"].isMember(k8s_label)) + { + return false; + } + + // The pod spec is stored as a stringified json label on the container + cfg_str = config_obj["Labels"][k8s_label].asString(); + + if(cfg_str == "") + { + return false; + } + + Json::Value cfg; + if(!reader.parse(cfg_str.c_str(), cfg)) + { + g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse pod config '%s'", cfg_str.c_str()); + return false; + } + + if(!cfg.isMember("spec") || + !cfg["spec"].isMember("containers") || + !cfg["spec"]["containers"].isArray()) + { + return false; + } + + spec = cfg["spec"]["containers"][0]; + + return true; +} + +bool sinsp_container_info::container_health_probe::parse_healthcheck(const Json::Value &healthcheck_obj) +{ + if(healthcheck_obj.isNull() || + !healthcheck_obj.isMember("Test")) + { + g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s", + Json::FastWriter().write(healthcheck_obj).c_str()); + + return false; + } + + const Json::Value &test_obj = healthcheck_obj["Test"]; + + if(!test_obj.isArray() || test_obj.size() < 2) + { + g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse health check from %s", + Json::FastWriter().write(healthcheck_obj).c_str()); + return false; + } + + if(test_obj[0].asString() == "CMD") + { + m_health_probe_exe = normalize_arg(test_obj[1].asString()); + + for(uint32_t i = 2; i < test_obj.size(); i++) { - return &mntinfo; + m_health_probe_args.push_back(normalize_arg(test_obj[i].asString())); } } + else if(test_obj[0].asString() == "CMD-SHELL") + { + m_health_probe_exe = "/bin/sh"; + m_health_probe_args.push_back("-c"); + m_health_probe_args.push_back(test_obj[1].asString()); + } + else + { + // This occurs when HEALTHCHECK is NONE. No warning log in this case. + return false; + } - return NULL; + m_obj = healthcheck_obj; + m_probe_type = PT_HEALTHCHECK; + return true; } -const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_dest(std::string &dest) const +bool sinsp_container_info::container_health_probe::parse_liveness_readiness_probe(const Json::Value &probe_obj, probe_type ptype) { - // note: linear search - for (auto &mntinfo :m_mounts) + if(probe_obj.isNull() || + !probe_obj.isMember("exec") || + !probe_obj["exec"].isMember("command")) { - if(sinsp_utils::glob_match(dest.c_str(), mntinfo.m_dest.c_str())) + g_logger.format(sinsp_logger::SEV_WARNING, "Could not parse liveness/readiness probe from %s", + Json::FastWriter().write(probe_obj).c_str()); + return false; + } + + const Json::Value command_obj = probe_obj["exec"]["command"]; + + if(!command_obj.isNull() && command_obj.isArray()) + { + m_health_probe_exe = normalize_arg(command_obj[0].asString()); + for(uint32_t i = 1; i < command_obj.size(); i++) { - return &mntinfo; + m_health_probe_args.push_back(normalize_arg(command_obj[i].asString())); } } - return NULL; + m_obj = probe_obj; + m_probe_type = ptype; + return true; } -std::string sinsp_container_info::normalize_healthcheck_arg(const std::string &arg) +std::string sinsp_container_info::container_health_probe::normalize_arg(const std::string &arg) { std::string ret = arg; @@ -81,31 +278,61 @@ std::string sinsp_container_info::normalize_healthcheck_arg(const std::string &a return ret; } -void sinsp_container_info::parse_healthcheck(const Json::Value &healthcheck_obj) +const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_idx(uint32_t idx) const { - if(!healthcheck_obj.isNull()) + if (idx >= m_mounts.size()) { - const Json::Value &test_obj = healthcheck_obj["Test"]; + return NULL; + } - if(!test_obj.isNull() && test_obj.isArray() && test_obj.size() >= 2) + return &(m_mounts[idx]); +} + +const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_source(std::string &source) const +{ + // note: linear search + for (auto &mntinfo :m_mounts) + { + if(sinsp_utils::glob_match(source.c_str(), mntinfo.m_source.c_str())) { - if(test_obj[0].asString() == "CMD") - { - m_has_healthcheck = true; - m_healthcheck_exe = normalize_healthcheck_arg(test_obj[1].asString()); + return &mntinfo; + } + } - for(uint32_t i = 2; i < test_obj.size(); i++) - { - m_healthcheck_args.push_back(normalize_healthcheck_arg(test_obj[i].asString())); - } - } - else if(test_obj[0].asString() == "CMD-SHELL") - { - m_has_healthcheck = true; - m_healthcheck_exe = "/bin/sh"; - m_healthcheck_args.push_back("-c"); - m_healthcheck_args.push_back(test_obj[1].asString()); - } + return NULL; +} + +const sinsp_container_info::container_mount_info *sinsp_container_info::mount_by_dest(std::string &dest) const +{ + // note: linear search + for (auto &mntinfo :m_mounts) + { + if(sinsp_utils::glob_match(dest.c_str(), mntinfo.m_dest.c_str())) + { + return &mntinfo; } } + + return NULL; } + +sinsp_container_info::container_health_probe::probe_type sinsp_container_info::match_health_probe(sinsp_threadinfo *tinfo) +{ + auto pred = [&] (container_health_probe &p) { + return (p.m_health_probe_exe == tinfo->m_exe && + p.m_health_probe_args == tinfo->m_args); + }; + + auto match = std::find_if(m_health_probes.begin(), + m_health_probes.end(), + pred); + + if(match == m_health_probes.end()) + { + return container_health_probe::PT_NONE; + } + + return match->m_probe_type; +} + + diff --git a/userspace/libsinsp/container_info.h b/userspace/libsinsp/container_info.h index 0171268e6c..a1793b7b83 100644 --- a/userspace/libsinsp/container_info.h +++ b/userspace/libsinsp/container_info.h @@ -21,6 +21,7 @@ limitations under the License. #include #include +#include #include #include @@ -38,6 +39,8 @@ enum sinsp_container_type CT_CONTAINERD = 7, }; +class sinsp_threadinfo; + // Docker and CRI-compatible runtimes are very similar static inline bool is_docker_compatible(sinsp_container_type t) { @@ -122,6 +125,58 @@ class sinsp_container_info std::string m_propagation; }; + class container_health_probe + { + public: + + // The type of health probe + enum probe_type { + PT_NONE = 0, + PT_HEALTHCHECK, + PT_LIVENESS_PROBE, + PT_READINESS_PROBE + }; + + container_health_probe(); + container_health_probe(const container_health_probe &other); + container_health_probe(container_health_probe &&other); + virtual ~container_health_probe(); + + container_health_probe &operator=(const container_health_probe &other); + + // Attempt to parse any health probes out of the + // provided config obj and add them to the provided + // list. + static void add_health_probes(const Json::Value &config_obj, + std::list &probes); + + void add_to_json(Json::Value &container_obj) const; + + // The probe_type that should be used for commands + // matching this health probe. + probe_type m_probe_type; + + // The actual health probe exe and args. + std::string m_health_probe_exe; + std::vector m_health_probe_args; + + protected: + // The part of the container configuration that should + // be saved in the CONTAINER_JSON event for this + // health probe. + Json::Value m_obj; + + static bool get_k8s_pod_spec(const Json::Value &config_obj, Json::Value &spec); + + // Parse the health probe executable/arguments out of + // the provided object. Returns true if a probe check + // could be parsed successfully, false otherwise. + bool parse_healthcheck(const Json::Value &healthcheck_obj); + bool parse_liveness_readiness_probe(const Json::Value &probe_obj, probe_type ptype); + + std::string normalize_arg(const std::string &arg); + }; + sinsp_container_info(): m_container_ip(0), m_privileged(false), @@ -130,8 +185,6 @@ class sinsp_container_info m_cpu_shares(1024), m_cpu_quota(0), m_cpu_period(100000), - m_has_healthcheck(false), - m_healthcheck_exe(""), m_is_pod_sandbox(false) #ifdef HAS_ANALYZER ,m_metadata_deadline(0) @@ -139,9 +192,6 @@ class sinsp_container_info { } - std::string normalize_healthcheck_arg(const std::string &arg); - void parse_healthcheck(const Json::Value &config_obj); - const std::vector& get_env() const { return m_env; } const container_mount_info *mount_by_idx(uint32_t idx) const; @@ -152,6 +202,9 @@ class sinsp_container_info return m_is_pod_sandbox; } + // Match a process against the set of health probes + container_health_probe::probe_type match_health_probe(sinsp_threadinfo *tinfo); + std::string m_id; sinsp_container_type m_type; std::string m_name; @@ -172,10 +225,8 @@ class sinsp_container_info int64_t m_cpu_shares; int64_t m_cpu_quota; int64_t m_cpu_period; - Json::Value m_healthcheck_obj; - bool m_has_healthcheck; - std::string m_healthcheck_exe; - std::vector m_healthcheck_args; + std::list m_health_probes; + bool m_is_pod_sandbox; #ifdef HAS_ANALYZER std::string m_sysdig_agent_conf; diff --git a/userspace/libsinsp/filterchecks.cpp b/userspace/libsinsp/filterchecks.cpp index f80cb4af33..3c73655333 100644 --- a/userspace/libsinsp/filterchecks.cpp +++ b/userspace/libsinsp/filterchecks.cpp @@ -1810,6 +1810,8 @@ const filtercheck_field_info sinsp_filter_check_thread_fields[] = {PT_CHARBUF, EPF_TABLE_ONLY, PF_NA, "thread.nametid", "this field chains the process name and tid of a thread and can be used as a specific identifier of a thread for a specific execve."}, {PT_INT64, EPF_NONE, PF_ID, "proc.vpgid", "the process group id of the process generating the event, as seen from its current PID namespace."}, {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_healthcheck", "true if this process is running as a part of the container's health check."}, + {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_liveness_probe", "true if this process is running as a part of the container's liveness probe."}, + {PT_BOOL, EPF_NONE, PF_NA, "proc.is_container_readiness_probe", "true if this process is running as a part of the container's readiness probe."}, }; sinsp_filter_check_thread::sinsp_filter_check_thread() @@ -2599,7 +2601,13 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b m_tstr = tinfo->get_comm() + to_string(evt->get_tid()); RETURN_EXTRACT_STRING(m_tstr); case TYPE_IS_CONTAINER_HEALTHCHECK: - m_tbool = tinfo->m_is_container_healthcheck; + m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_HEALTHCHECK); + RETURN_EXTRACT_VAR(m_tbool); + case TYPE_IS_CONTAINER_LIVENESS_PROBE: + m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_LIVENESS_PROBE); + RETURN_EXTRACT_VAR(m_tbool); + case TYPE_IS_CONTAINER_READINESS_PROBE: + m_tbool = (tinfo->m_category == sinsp_threadinfo::CAT_READINESS_PROBE); RETURN_EXTRACT_VAR(m_tbool); default: ASSERT(false); @@ -5900,7 +5908,9 @@ const filtercheck_field_info sinsp_filter_check_container_fields[] = {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.repository", "the container image repository (e.g. sysdig/sysdig)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.tag", "the container image tag (e.g. stable, latest)."}, {PT_CHARBUF, EPF_NONE, PF_NA, "container.image.digest", "the container image registry digest (e.g. sha256:d977378f890d445c15e51795296e4e5062f109ce6da83e0a355fc4ad8699d27)."}, - {PT_CHARBUF, EPF_NONE, PF_NA, "container.healthcheck", "The container's health check. Will be the null value (\"N/A\") if no healthcheck configured, \"NONE\" if configured but explicitly not created, and the healthcheck command line otherwise"} + {PT_CHARBUF, EPF_NONE, PF_NA, "container.healthcheck", "The container's health check. Will be the null value (\"N/A\") if no healthcheck configured, \"NONE\" if configured but explicitly not created, and the healthcheck command line otherwise"}, + {PT_CHARBUF, EPF_NONE, PF_NA, "container.liveness_probe", "The container's liveness probe. Will be the null value (\"N/A\") if no liveness probe configured, the liveness probe command line otherwise"}, + {PT_CHARBUF, EPF_NONE, PF_NA, "container.readiness_probe", "The container's readiness probe. Will be the null value (\"N/A\") if no readiness probe configured, the readiness probe command line otherwise"} }; sinsp_filter_check_container::sinsp_filter_check_container() @@ -6325,6 +6335,8 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len } break; case TYPE_CONTAINER_HEALTHCHECK: + case TYPE_CONTAINER_LIVENESS_PROBE: + case TYPE_CONTAINER_READINESS_PROBE: if(tinfo->m_container_id.empty()) { return NULL; @@ -6338,26 +6350,31 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len return NULL; } - if(container_info->m_healthcheck_obj.isNull()) - { - return NULL; - } - - if(!container_info->m_has_healthcheck) + for(auto &probe : container_info->m_health_probes) { - m_tstr = "NONE"; - - RETURN_EXTRACT_STRING(m_tstr); - } + if((m_field_id == TYPE_CONTAINER_HEALTHCHECK && + probe.m_probe_type == sinsp_container_info::container_health_probe::PT_HEALTHCHECK) || + (m_field_id == TYPE_CONTAINER_LIVENESS_PROBE && + probe.m_probe_type == sinsp_container_info::container_health_probe::PT_LIVENESS_PROBE) || + (m_field_id == TYPE_CONTAINER_READINESS_PROBE && + probe.m_probe_type == sinsp_container_info::container_health_probe::PT_READINESS_PROBE)) + { + m_tstr = probe.m_health_probe_exe; - m_tstr = container_info->m_healthcheck_exe; + for(auto &arg : probe.m_health_probe_args) + { + m_tstr += " "; + m_tstr += arg; + } - for(auto &arg : container_info->m_healthcheck_args) - { - m_tstr += " "; - m_tstr += arg; + RETURN_EXTRACT_STRING(m_tstr); + } } + // If here, then the container didn't have any + // health probe matching the filtercheck + // field. + m_tstr = "NONE"; RETURN_EXTRACT_STRING(m_tstr); } diff --git a/userspace/libsinsp/filterchecks.h b/userspace/libsinsp/filterchecks.h index 6e108e592e..f4e6031790 100644 --- a/userspace/libsinsp/filterchecks.h +++ b/userspace/libsinsp/filterchecks.h @@ -339,6 +339,8 @@ class sinsp_filter_check_thread : public sinsp_filter_check TYPE_NAMETID = 44, TYPE_VPGID = 45, TYPE_IS_CONTAINER_HEALTHCHECK = 46, + TYPE_IS_CONTAINER_LIVENESS_PROBE = 47, + TYPE_IS_CONTAINER_READINESS_PROBE = 48, }; sinsp_filter_check_thread(); @@ -724,6 +726,8 @@ class sinsp_filter_check_container : public sinsp_filter_check TYPE_CONTAINER_IMAGE_TAG, TYPE_CONTAINER_IMAGE_DIGEST, TYPE_CONTAINER_HEALTHCHECK, + TYPE_CONTAINER_LIVENESS_PROBE, + TYPE_CONTAINER_READINESS_PROBE, }; sinsp_filter_check_container(); diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index b9017c4c2c..b701905f39 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -2261,7 +2261,7 @@ inline void sinsp_parser::add_socket(sinsp_evt *evt, int64_t fd, uint32_t domain * * Preconditions: evt->m_fdinfo == nullptr and * evt->m_tinfo != nullptr - * + * */ inline void sinsp_parser::infer_sendto_fdinfo(sinsp_evt* const evt) { @@ -4533,7 +4533,9 @@ void sinsp_parser::parse_container_json_evt(sinsp_evt *evt) libsinsp::container_engine::docker::parse_json_mounts(container["Mounts"], container_info.m_mounts); - container_info.parse_healthcheck(container["Healthcheck"]); + // Add any health checks described in the container config/labels. + sinsp_container_info::container_health_probe::add_health_probes(container, container_info.m_health_probes); + const Json::Value& contip = container["ip"]; if(!contip.isNull() && contip.isConvertibleTo(Json::stringValue)) { diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index ef68be9ba1..6738a636d8 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -90,7 +90,7 @@ void sinsp_threadinfo::init() m_lastevent_data = NULL; m_parent_loop_detected = false; m_tty = 0; - m_is_container_healthcheck = false; + m_category = CAT_NONE; m_blprogram = NULL; m_loginuid = 0; } @@ -424,7 +424,7 @@ void sinsp_threadinfo::init(scap_threadinfo* pi) m_clone_ts = pi->clone_ts; m_tty = pi->tty; m_loginuid = pi->loginuid; - m_is_container_healthcheck = false; + m_category = CAT_NONE; set_cgroups(pi->cgroups, pi->cgroups_len); m_root = pi->root; diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index ca90478513..ce7b8cd07a 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -265,8 +265,16 @@ class SINSP_PUBLIC sinsp_threadinfo int32_t m_tty; int32_t m_loginuid; ///< loginuid (auid) - // If true, this thread is part of a container health check - bool m_is_container_healthcheck; + // In some cases, a threadinfo has a category that identfies + // why it was run. + enum command_category { + CAT_NONE = 0, + CAT_HEALTHCHECK, + CAT_LIVENESS_PROBE, + CAT_READINESS_PROBE + }; + + command_category m_category; // // State for multi-event processing