From 683408b431cc5dfa9430f7a12a942a80b837d2e7 Mon Sep 17 00:00:00 2001 From: Jorge Niedbalski Date: Thu, 19 Dec 2024 09:08:21 +0100 Subject: [PATCH] core: add conditionals evaluation library. this is a library to evaluate conditionals based on rules, rules are declared as matching elements using record accessor. Signed-off-by: Jorge Niedbalski --- include/fluent-bit/flb_conditionals.h | 91 +++++++ src/CMakeLists.txt | 1 + src/flb_conditionals.c | 363 ++++++++++++++++++++++++++ tests/internal/CMakeLists.txt | 1 + 4 files changed, 456 insertions(+) create mode 100644 include/fluent-bit/flb_conditionals.h create mode 100644 src/flb_conditionals.c diff --git a/include/fluent-bit/flb_conditionals.h b/include/fluent-bit/flb_conditionals.h new file mode 100644 index 00000000000..26c0178c42f --- /dev/null +++ b/include/fluent-bit/flb_conditionals.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit 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. + */ + +#ifndef FLB_CONDITIONS_H +#define FLB_CONDITIONS_H + +#include +#include +#include +#include +#include +#include +#include + +/* Context types enum */ +enum record_context_type { + RECORD_CONTEXT_BODY = 0, + RECORD_CONTEXT_METADATA = 1 +}; + +struct flb_condition; + +enum flb_condition_operator { + FLB_COND_OP_AND, + FLB_COND_OP_OR +}; + +enum flb_rule_operator { + FLB_RULE_OP_EQ, + FLB_RULE_OP_NEQ, + FLB_RULE_OP_GT, + FLB_RULE_OP_LT, + FLB_RULE_OP_REGEX, + FLB_RULE_OP_IN, + FLB_RULE_OP_NOT_IN +}; + +struct flb_condition_rule { + struct flb_cfl_record_accessor *ra; /* Record accessor for the field */ + enum record_context_type context; /* Whether rule applies to body or metadata */ + enum flb_rule_operator op; + union { + flb_sds_t str_val; + double num_val; + struct { + flb_sds_t *values; + int count; + } array; + } value; + struct flb_regex *regex; + struct mk_list _head; +}; + +struct flb_condition { + enum flb_condition_operator op; + struct mk_list rules; +}; + +/* Core condition functions */ +struct flb_condition *flb_condition_create(enum flb_condition_operator op); + +int flb_condition_add_rule(struct flb_condition *cond, + const char *field, + enum flb_rule_operator op, + void *value, + int value_count, + enum record_context_type context); + +void flb_condition_destroy(struct flb_condition *cond); + +/* Evaluation function */ +int flb_condition_evaluate(struct flb_condition *cond, + struct flb_mp_chunk_record *record); + +#endif /* FLB_CONDITIONS_H */ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ba91972ac0..b3df3ebbb91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,7 @@ set(src flb_lock.c flb_cfl_ra_key.c flb_cfl_record_accessor.c + flb_conditionals.c ) # Config format diff --git a/src/flb_conditionals.c b/src/flb_conditionals.c new file mode 100644 index 00000000000..a1c2c791b79 --- /dev/null +++ b/src/flb_conditionals.c @@ -0,0 +1,363 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit 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 +#include +#include +#include +#include +#include +#include + +/* Function to get the record variant based on context */ +static inline struct cfl_variant *get_record_variant(struct flb_mp_chunk_record *record, + enum record_context_type context_type) +{ + if (!record) { + return NULL; + } + + switch (context_type) { + case RECORD_CONTEXT_METADATA: + if (record->cobj_metadata) { + return record->cobj_metadata->variant; + } + break; + + case RECORD_CONTEXT_BODY: + if (record->cobj_record) { + return record->cobj_record->variant; + } + break; + } + + return NULL; +} + +static struct flb_condition_rule *rule_create(const char *field, + enum flb_rule_operator op, + void *value, + int value_count, + enum record_context_type context) +{ + struct flb_condition_rule *rule; + int i,j; + + if (!field || !value) { + return NULL; + } + + switch (op) { + case FLB_RULE_OP_EQ: + case FLB_RULE_OP_NEQ: + case FLB_RULE_OP_REGEX: + if (!value || !((char *)value)[0]) { + return NULL; + } + break; + case FLB_RULE_OP_GT: + case FLB_RULE_OP_LT: + if (!value) { + return NULL; + } + break; + + case FLB_RULE_OP_IN: + case FLB_RULE_OP_NOT_IN: + if (!value || value_count <= 0 || !((char **)value)[0]) { + return NULL; + } + for (i = 0; i < value_count; i++) { + if (!((char **)value)[i]) { + return NULL; + } + } + break; + + default: + return NULL; + } + + rule = flb_calloc(1, sizeof(struct flb_condition_rule)); + if (!rule) { + cfl_errno(); + return NULL; + } + + rule->ra = flb_cfl_ra_create((char *)field, FLB_TRUE); + if (!rule->ra) { + flb_free(rule); + return NULL; + } + + rule->context = context; + rule->op = op; + + switch (op) { + case FLB_RULE_OP_NEQ: + case FLB_RULE_OP_EQ: + rule->value.str_val = flb_sds_create((char *)value); + if (!rule->value.str_val) { + flb_cfl_ra_destroy(rule->ra); + flb_free(rule); + return NULL; + } + break; + + case FLB_RULE_OP_GT: + case FLB_RULE_OP_LT: + rule->value.num_val = *(double *)value; + break; + + case FLB_RULE_OP_REGEX: + rule->regex = flb_regex_create((char *)value); + if (!rule->regex) { + flb_cfl_ra_destroy(rule->ra); + flb_free(rule); + return NULL; + } + break; + + case FLB_RULE_OP_IN: + case FLB_RULE_OP_NOT_IN: + rule->value.array.values = flb_calloc(value_count, sizeof(flb_sds_t)); + if (!rule->value.array.values) { + flb_cfl_ra_destroy(rule->ra); + flb_free(rule); + return NULL; + } + + for (i = 0; i < value_count; i++) { + rule->value.array.values[i] = flb_sds_create(((char **)value)[i]); + if (!rule->value.array.values[i]) { + for (j = 0; j < i; j++) { + flb_sds_destroy(rule->value.array.values[j]); + } + flb_free(rule->value.array.values); + flb_cfl_ra_destroy(rule->ra); + flb_free(rule); + return NULL; + } + } + rule->value.array.count = value_count; + break; + } + + return rule; +} + +static void rule_destroy(struct flb_condition_rule *rule) +{ + int i; + + if (!rule) { + return; + } + + if (rule->ra) { + flb_cfl_ra_destroy(rule->ra); + } + + switch (rule->op) { + case FLB_RULE_OP_EQ: + case FLB_RULE_OP_NEQ: + if (rule->value.str_val) { + flb_sds_destroy(rule->value.str_val); + } + break; + + case FLB_RULE_OP_REGEX: + if (rule->regex) { + flb_regex_destroy(rule->regex); + } + break; + + case FLB_RULE_OP_IN: + case FLB_RULE_OP_NOT_IN: + for (i = 0; i < rule->value.array.count; i++) { + flb_sds_destroy(rule->value.array.values[i]); + } + flb_free(rule->value.array.values); + break; + + case FLB_RULE_OP_GT: + case FLB_RULE_OP_LT: + break; + + default: + break; + } + + flb_free(rule); +} + +struct flb_condition *flb_condition_create(enum flb_condition_operator op) +{ + struct flb_condition *cond; + + cond = flb_calloc(1, sizeof(struct flb_condition)); + if (!cond) { + cfl_errno(); + return NULL; + } + + cond->op = op; + mk_list_init(&cond->rules); + + return cond; +} + +int flb_condition_add_rule(struct flb_condition *cond, + const char *field, + enum flb_rule_operator op, + void *value, + int value_count, + enum record_context_type context) +{ + struct flb_condition_rule *rule; + + if (!cond || !field || !value) { + return FLB_FALSE; + } + + rule = rule_create(field, op, value, value_count, context); + if (!rule) { + return FLB_FALSE; + } + + mk_list_add(&rule->_head, &cond->rules); + return FLB_TRUE; +} + +void flb_condition_destroy(struct flb_condition *cond) +{ + struct mk_list *tmp; + struct mk_list *head; + struct flb_condition_rule *rule; + + if (!cond) { + return; + } + + mk_list_foreach_safe(head, tmp, &cond->rules) { + rule = mk_list_entry(head, struct flb_condition_rule, _head); + mk_list_del(&rule->_head); + rule_destroy(rule); + } + + flb_free(cond); +} + +static int evaluate_rule(struct flb_condition_rule *rule, + struct cfl_variant *record_variant) +{ + flb_sds_t str_val; + int i; + int result = FLB_FALSE; + double num_val; + + if (!rule || !record_variant) { + return FLB_FALSE; + } + + str_val = flb_cfl_ra_translate(rule->ra, NULL, 0, *record_variant, NULL); + if (!str_val) { + return FLB_FALSE; + } + + switch (rule->op) { + case FLB_RULE_OP_EQ: + result = (strcmp(str_val, rule->value.str_val) == 0); + break; + + case FLB_RULE_OP_NEQ: + result = (strcmp(str_val, rule->value.str_val) != 0); + break; + + case FLB_RULE_OP_GT: + num_val = atof(str_val); + result = (num_val > rule->value.num_val); + break; + + case FLB_RULE_OP_LT: + num_val = atof(str_val); + result = (num_val < rule->value.num_val); + break; + + case FLB_RULE_OP_REGEX: + result = (flb_regex_match(rule->regex, + (unsigned char *)str_val, + flb_sds_len(str_val)) > 0); + break; + + case FLB_RULE_OP_IN: + case FLB_RULE_OP_NOT_IN: + for (i = 0; i < rule->value.array.count; i++) { + if (strcmp(str_val, rule->value.array.values[i]) == 0) { + result = (rule->op == FLB_RULE_OP_IN); + break; + } + } + if (i == rule->value.array.count) { + result = (rule->op == FLB_RULE_OP_NOT_IN); + } + break; + } + + flb_sds_destroy(str_val); + return result; +} + +int flb_condition_evaluate(struct flb_condition *cond, + struct flb_mp_chunk_record *record) +{ + struct mk_list *head; + struct flb_condition_rule *rule; + struct cfl_variant *record_variant; + int result; + + if (!cond || !record) { + return FLB_TRUE; + } + + if (mk_list_size(&cond->rules) == 0) { + return (cond->op == FLB_COND_OP_AND); + } + + mk_list_foreach(head, &cond->rules) { + rule = mk_list_entry(head, struct flb_condition_rule, _head); + + /* Get the variant for this rule's context */ + record_variant = get_record_variant(record, rule->context); + if (!record_variant) { + continue; + } + + result = evaluate_rule(rule, record_variant); + + if (cond->op == FLB_COND_OP_AND && result == FLB_FALSE) { + return FLB_FALSE; + } + else if (cond->op == FLB_COND_OP_OR && result == FLB_TRUE) { + return FLB_TRUE; + } + } + + return (cond->op == FLB_COND_OP_AND) ? FLB_TRUE : FLB_FALSE; +} diff --git a/tests/internal/CMakeLists.txt b/tests/internal/CMakeLists.txt index c8225739b8c..efdc7bf0574 100644 --- a/tests/internal/CMakeLists.txt +++ b/tests/internal/CMakeLists.txt @@ -43,6 +43,7 @@ set(UNIT_TESTS_FILES processor.c uri.c msgpack_append_message.c + conditionals.c endianness )