From 0066ba49ea6e230a512250a93b19ec7d18f48b3d Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Wed, 15 Jun 2022 16:17:14 -0700 Subject: [PATCH] Falco engine changes to support load_rules result class Add new load_rules methods that return a result object instead of throwing exceptions on error. The existing load_rules methods call the new methods internally and continue to throw exceptions on error/return individual values on success. The result is returned as a unique_ptr so it can be populated while loading rules (as a part of the configuration object) and then move()d to the return value. Signed-off-by: Mark Stemm --- userspace/engine/falco_engine.cpp | 87 ++++++++++++++++++++----------- userspace/engine/falco_engine.h | 9 +++- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index c51a7afd..0abfc1ca 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -18,6 +18,7 @@ limitations under the License. #include #include #include +#include #include #include @@ -36,6 +37,7 @@ limitations under the License. const std::string falco_engine::s_default_ruleset = "falco-default-ruleset"; using namespace std; +using namespace falco; falco_engine::falco_engine(bool seed_rng) : m_next_ruleset_id(0), @@ -165,66 +167,89 @@ void falco_engine::list_fields(std::string &source, bool verbose, bool names_onl void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events) { - rule_loader::configuration cfg(rules_content, m_sources); + static const std::string no_name = "N/A"; + + std::unique_ptr res = load_rules(rules_content, no_name); + + if(verbose) + { + // Here, verbose controls whether to additionally + // "log" e.g. print to stderr. What's logged is always + // non-verbose so it fits on a single line. + // todo(jasondellaluce): introduce a logging callback in Falco + fprintf(stderr, "%s\n", res->as_string(false).c_str()); + } + + if(!res->successful()) + { + // The output here is always the full e.g. "verbose" output. + throw falco_exception(res->as_string(true).c_str()); + } +} + +std::unique_ptr falco_engine::load_rules(const std::string &rules_content, const std::string &name) +{ + rule_loader::configuration cfg(rules_content, m_sources, name); cfg.min_priority = m_min_priority; cfg.output_extra = m_extra; cfg.replace_output_container_info = m_replace_container_info; cfg.default_ruleset_id = m_default_ruleset_id; - std::ostringstream os; rule_reader reader; - bool success = reader.load(cfg, m_rule_loader); - if (success) + if (reader.load(cfg, m_rule_loader)) { for (auto &src : m_sources) { src.ruleset = src.ruleset_factory->new_ruleset(); } m_rules.clear(); - success = m_rule_loader.compile(cfg, m_rules); + m_rule_loader.compile(cfg, m_rules); } - if (!cfg.errors.empty()) + + return std::move(cfg.res); +} + +void falco_engine::load_rules_file(const std::string &rules_filename, bool verbose, bool all_events) +{ + std::unique_ptr res = load_rules_file(rules_filename); + + if(verbose) { - os << cfg.errors.size() << " errors:" << std::endl; - for(auto &err : cfg.errors) - { - os << err << std::endl; - } - } - if (!cfg.warnings.empty()) - { - os << cfg.warnings.size() << " warnings:" << std::endl; - for(auto &warn : cfg.warnings) - { - os << warn << std::endl; - } - } - if(!success) - { - throw falco_exception(os.str()); - } - if (verbose && os.str() != "") { + // Here, verbose controls whether to additionally + // "log" e.g. print to stderr. What's logged is always + // non-verbose so it fits on a single line. // todo(jasondellaluce): introduce a logging callback in Falco - fprintf(stderr, "When reading rules content: %s", os.str().c_str()); + fprintf(stderr, "%s\n", res->as_string(false).c_str()); + } + + if(!res->successful()) + { + // The output here is always the full e.g. "verbose" output. + throw falco_exception(res->as_string(true).c_str()); } } -void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events) +std::unique_ptr falco_engine::load_rules_file(const string &rules_filename) { ifstream is; is.open(rules_filename); if (!is.is_open()) { - throw falco_exception("Could not open rules filename " + - rules_filename + " " + - "for reading"); + rule_loader::context ctx(rules_filename); + std::string empty; + + std::unique_ptr res(new rule_loader::result(rules_filename)); + + res->add_error(load_result::LOAD_ERR_FILE_READ, "Could not open for reading.", ctx, empty); + + return std::move(res); } string rules_content((istreambuf_iterator(is)), istreambuf_iterator()); - load_rules(rules_content, verbose, all_events); + return load_rules(rules_content, rules_filename); } void falco_engine::enable_rule(const string &substring, bool enabled, const string &ruleset) diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 44fe1489..e815b09c 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -34,6 +34,7 @@ limitations under the License. #include "stats_manager.h" #include "falco_common.h" #include "falco_source.h" +#include "falco_load_result.h" // // This class acts as the primary interface between a program and the @@ -63,6 +64,12 @@ public: void load_rules_file(const std::string &rules_filename, bool verbose, bool all_events); void load_rules(const std::string &rules_content, bool verbose, bool all_events); + // + // Identical to above, but returns a result object instead of + // throwing exceptions on error. + std::unique_ptr load_rules_file(const std::string &rules_filename); + std::unique_ptr load_rules(const std::string &rules_content, const std::string &name); + // // Enable/Disable any rules matching the provided substring. // If the substring is "", all rules are enabled/disabled. @@ -181,7 +188,7 @@ public: std::size_t add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory); - + // // Equivalent to above, but allows specifying a ruleset factory // for the newly added source.