new(userspace/falco,unit_tests): added new tests around schema validation feature.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
This commit is contained in:
Federico Di Pierro 2024-08-21 12:42:22 +02:00 committed by poiana
parent 94dc7da986
commit be927edfe8
6 changed files with 133 additions and 9 deletions

View File

@ -47,6 +47,7 @@ add_executable(falco_unit_tests
falco/test_configuration_rule_selection.cpp
falco/test_configuration_config_files.cpp
falco/test_configuration_env_vars.cpp
falco/test_configuration_schema.cpp
falco/app/actions/test_select_event_sources.cpp
falco/app/actions/test_load_config.cpp
)

View File

@ -0,0 +1,120 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2023 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <gtest/gtest.h>
#include <falco/configuration.h>
#include <falco_test_var.h>
#define EXPECT_VALIDATION_STATUS(res, status) \
do { \
for(const auto& pair : res) { \
auto validation_status = pair.second; \
EXPECT_TRUE(sinsp_utils::startswith(validation_status, status)); \
} \
} \
while (0)
// Read Falco config from current repo-path
TEST(Configuration, schema_validate_config)
{
falco_configuration falco_config;
config_loaded_res res;
EXPECT_NO_THROW(res = falco_config.init_from_file(TEST_FALCO_CONFIG, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok);
}
TEST(Configuration, schema_ok)
{
falco_configuration falco_config;
config_loaded_res res;
/* OK YAML */
std::string config =
"falco_libs:\n"
" thread_table_size: 50\n";
EXPECT_NO_THROW(res = falco_config.init_from_content(config, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok);
}
TEST(Configuration, schema_wrong_key)
{
falco_configuration falco_config;
config_loaded_res res;
/* Miss-typed key YAML */
std::string config =
"falco_libss:\n"
" thread_table_size: 50\n";
EXPECT_NO_THROW(res = falco_config.init_from_content(config, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_failed);
}
TEST(Configuration, schema_wrong_type)
{
falco_configuration falco_config;
/* Wrong value type YAML */
std::string config =
"falco_libs: 512\n";
// We expect an exception since `falco_configuration::load_yaml()`
// will fail to parse `falco_libs` node.
ASSERT_ANY_THROW(falco_config.init_from_content(config, {}));
}
TEST(Configuration, schema_wrong_embedded_key)
{
falco_configuration falco_config;
config_loaded_res res;
/* Miss-typed sub-key YAML */
std::string config =
"falco_libs:\n"
" thread_table_sizeee: 50\n";
EXPECT_NO_THROW(res = falco_config.init_from_content(config, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_failed);
}
TEST(Configuration, schema_yaml_helper_validator)
{
yaml_helper conf;
falco_configuration falco_config;
/* Broken YAML */
std::string sample_yaml =
"falco_libs:\n"
" thread_table_size: 50\n";
// Ok, we don't ask for any validation
EXPECT_NO_THROW(conf.load_from_string(sample_yaml));
// We pass a string variable but not a schema
std::string validation;
EXPECT_NO_THROW(conf.load_from_string(sample_yaml, Json::Value{}, &validation));
EXPECT_EQ(validation, yaml_helper::validation_none);
// We pass a schema but not a string storage for the validation; no validation takes place
EXPECT_NO_THROW(conf.load_from_string(sample_yaml, falco_config.m_config_schema, nullptr));
// We pass everything
EXPECT_NO_THROW(conf.load_from_string(sample_yaml, falco_config.m_config_schema, &validation));
EXPECT_EQ(validation, yaml_helper::validation_ok);
}

View File

@ -2,3 +2,4 @@
#define TEST_ENGINE_KMOD_CONFIG "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/engine_kmod_config.yaml"
#define TEST_ENGINE_MODERN_CONFIG "${CMAKE_SOURCE_DIR}/unit_tests/falco/test_configs/engine_modern_config.yaml"
#define TEST_FALCO_CONFIG "${CMAKE_SOURCE_DIR}/falco.yaml"

View File

@ -65,8 +65,8 @@ falco::app::run_result falco::app::actions::load_config(const falco::app::state&
{
auto config_path = pair.first;
auto validation = pair.second;
auto priority = validation == "validated" ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + config_path + " | " + validation + "\n");
auto priority = validation == yaml_helper::validation_ok ? falco_logger::level::INFO : falco_logger::level::WARNING;
falco_logger::log(priority, std::string(" ") + config_path + " | validation: " + validation + "\n");
}
}

View File

@ -197,10 +197,9 @@ public:
// Needed by tests
yaml_helper m_config;
private:
Json::Value m_config_schema;
private:
void merge_config_files(const std::string& config_name, config_loaded_res &res);
void load_yaml(const std::string& config_name);
void init_logger();

View File

@ -85,6 +85,9 @@ class yaml_helper
{
public:
inline static const std::string configs_key = "config_files";
inline static const std::string validation_ok = "ok";
inline static const std::string validation_failed = "failed";
inline static const std::string validation_none = "schema not provided";
/**
* Load the YAML document represented by the input string.
@ -102,7 +105,7 @@ public:
}
else
{
*validation = "no schema provided";
*validation = validation_none;
}
}
}
@ -216,7 +219,7 @@ private:
}
else
{
*validation = "no schema provided";
*validation = validation_none;
}
}
return root;
@ -239,14 +242,14 @@ private:
// report only the top-most error
if (validationResults.popError(error))
{
return std::string("validation failed for ")
return std::string(validation_failed + " for ")
+ std::accumulate(error.context.begin(), error.context.end(), std::string(""))
+ ": "
+ error.description;
}
return "validation failed";
return validation_failed;
}
return "validated";
return validation_ok;
}
/*