diff --git a/src/flb_reload.c b/src/flb_reload.c index 9f44034e2c4..36c476ce1ff 100644 --- a/src/flb_reload.c +++ b/src/flb_reload.c @@ -533,6 +533,20 @@ int flb_reload(flb_ctx_t *ctx, struct flb_cf *cf_opts) new_config = new_ctx->config; + if (old_config->conf_path) { + new_config->conf_path = flb_strdup(old_config->conf_path); + if (!new_config->conf_path) { + if (file != NULL) { + flb_sds_destroy(file); + } + flb_cf_destroy(new_cf); + flb_destroy(new_ctx); + flb_error("[reload] copying configuration path failed. Reloading is halted"); + flb_reload_watchdog_cleanup(watchdog_ctx); + return FLB_RELOAD_HALTED; + } + } + /* Inherit verbose from the old ctx instance */ verbose = ctx->config->verbose; new_config->verbose = verbose; diff --git a/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.conf b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.conf new file mode 100644 index 00000000000..15065a66e60 --- /dev/null +++ b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.conf @@ -0,0 +1,29 @@ +[SERVICE] + Flush 1 + Log_Level info + Parsers_File relative-parser.conf + HTTP_Server On + HTTP_Listen 127.0.0.1 + HTTP_Port ${FLUENT_BIT_HTTP_MONITORING_PORT} + Hot_Reload On + +[INPUT] + Name dummy + Tag relative_parser + Samples 1 + Dummy {"message":"after"} + +[FILTER] + Name parser + Match relative_parser + Key_Name message + Parser reload_test + Reserve_Data On + +[OUTPUT] + Name opentelemetry + Match relative_parser + Host 127.0.0.1 + Port ${TEST_SUITE_HTTP_PORT} + Logs_URI /v1/logs + Logs_Body_Key $parsed diff --git a/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.yaml b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.yaml new file mode 100644 index 00000000000..a738850530f --- /dev/null +++ b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-after.yaml @@ -0,0 +1,32 @@ +service: + flush: 1 + log_level: info + parsers_file: relative-parser.conf + http_server: on + http_port: ${FLUENT_BIT_HTTP_MONITORING_PORT} + hot_reload: on + +pipeline: + inputs: + - name: dummy + tag: relative_parser + samples: 1 + dummy: | + { + "message": "after" + } + + filters: + - name: parser + match: relative_parser + key_name: message + parser: reload_test + reserve_data: on + + outputs: + - name: opentelemetry + match: relative_parser + host: 127.0.0.1 + port: ${TEST_SUITE_HTTP_PORT} + logs_uri: /v1/logs + logs_body_key: $parsed diff --git a/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.conf b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.conf new file mode 100644 index 00000000000..db462214bc0 --- /dev/null +++ b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.conf @@ -0,0 +1,29 @@ +[SERVICE] + Flush 1 + Log_Level info + Parsers_File relative-parser.conf + HTTP_Server On + HTTP_Listen 127.0.0.1 + HTTP_Port ${FLUENT_BIT_HTTP_MONITORING_PORT} + Hot_Reload On + +[INPUT] + Name dummy + Tag relative_parser + Samples 1 + Dummy {"message":"before"} + +[FILTER] + Name parser + Match relative_parser + Key_Name message + Parser reload_test + Reserve_Data On + +[OUTPUT] + Name opentelemetry + Match relative_parser + Host 127.0.0.1 + Port ${TEST_SUITE_HTTP_PORT} + Logs_URI /v1/logs + Logs_Body_Key $parsed diff --git a/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.yaml b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.yaml new file mode 100644 index 00000000000..c6f00d67d00 --- /dev/null +++ b/tests/integration/scenarios/hot_reload_watch/config/fluent-bit-relative-parser-before.yaml @@ -0,0 +1,32 @@ +service: + flush: 1 + log_level: info + parsers_file: relative-parser.conf + http_server: on + http_port: ${FLUENT_BIT_HTTP_MONITORING_PORT} + hot_reload: on + +pipeline: + inputs: + - name: dummy + tag: relative_parser + samples: 1 + dummy: | + { + "message": "before" + } + + filters: + - name: parser + match: relative_parser + key_name: message + parser: reload_test + reserve_data: on + + outputs: + - name: opentelemetry + match: relative_parser + host: 127.0.0.1 + port: ${TEST_SUITE_HTTP_PORT} + logs_uri: /v1/logs + logs_body_key: $parsed diff --git a/tests/integration/scenarios/hot_reload_watch/config/relative-parser.conf b/tests/integration/scenarios/hot_reload_watch/config/relative-parser.conf new file mode 100644 index 00000000000..b208ee1e055 --- /dev/null +++ b/tests/integration/scenarios/hot_reload_watch/config/relative-parser.conf @@ -0,0 +1,4 @@ +[PARSER] + Name reload_test + Format regex + Regex ^(?.*)$ diff --git a/tests/integration/scenarios/hot_reload_watch/tests/test_hot_reload_watch_001.py b/tests/integration/scenarios/hot_reload_watch/tests/test_hot_reload_watch_001.py index fe195f87bca..1e801470017 100644 --- a/tests/integration/scenarios/hot_reload_watch/tests/test_hot_reload_watch_001.py +++ b/tests/integration/scenarios/hot_reload_watch/tests/test_hot_reload_watch_001.py @@ -37,17 +37,24 @@ class Service: - def __init__(self, before_name, after_name): + def __init__(self, before_name, after_name, support_files=None): self.test_path = os.path.dirname(os.path.abspath(__file__)) self.config_dir = os.path.abspath(os.path.join(self.test_path, "../config")) self.before_config = os.path.join(self.config_dir, before_name) self.after_config = os.path.join(self.config_dir, after_name) + self.support_files = support_files or [] self.runtime_dir = tempfile.mkdtemp(prefix="flb-hot-reload-watch-") - self.runtime_config = os.path.join(self.runtime_dir, "fluent-bit.yaml") + extension = os.path.splitext(before_name)[1] + self.runtime_config = os.path.join(self.runtime_dir, f"fluent-bit{extension}") data_storage["logs"] = [] def start(self): shutil.copyfile(self.before_config, self.runtime_config) + for support_file in self.support_files: + shutil.copyfile( + os.path.join(self.config_dir, support_file), + os.path.join(self.runtime_dir, support_file), + ) self.flb = FluentBitManager(self.runtime_config) self.test_suite_http_port = find_available_port(starting_port=50000) @@ -96,7 +103,7 @@ def read_message(self, index): return payload["resourceLogs"][0]["scopeLogs"][0]["logRecords"][0]["body"]["stringValue"] def replace_config(self): - pending_path = os.path.join(self.runtime_dir, "fluent-bit.yaml.tmp") + pending_path = f"{self.runtime_config}.tmp" shutil.copyfile(self.after_config, pending_path) os.replace(pending_path, self.runtime_config) @@ -178,3 +185,37 @@ def test_hot_reload_http_yaml_config_change(): assert_reload_result(service) finally: service.stop() + + +@pytest.mark.parametrize( + ("before_name", "after_name"), + [ + ("fluent-bit-relative-parser-before.conf", "fluent-bit-relative-parser-after.conf"), + ("fluent-bit-relative-parser-before.yaml", "fluent-bit-relative-parser-after.yaml"), + ], + ids=["classic", "yaml"], +) +def test_hot_reload_http_relative_parser(before_name, after_name): + parser_file = "relative-parser.conf" + + # Ensure the parser cannot be loaded relative to the process working directory. + assert not os.path.exists(os.path.join(os.getcwd(), parser_file)) + + service = Service(before_name, after_name, support_files=[parser_file]) + + try: + service.start() + service.wait_for_log_count(1) + assert service.read_message(0) == "before" + + service.replace_config() + + with pytest.raises(TimeoutError): + service.flb.wait_for_hot_reload_count(1, timeout=2) + + payload = service.flb.trigger_http_reload() + assert payload["reload"] == "done" + service.flb.wait_for_hot_reload_count(1) + assert_reload_result(service) + finally: + service.stop() diff --git a/tests/internal/data/reload/fluent-bit.conf b/tests/internal/data/reload/fluent-bit.conf index b46ec97230e..9ea4a0fc54b 100644 --- a/tests/internal/data/reload/fluent-bit.conf +++ b/tests/internal/data/reload/fluent-bit.conf @@ -2,6 +2,7 @@ Flush 1 Daemon Off Log_Level error + Parsers_File parsers.conf HTTP_Server On HTTP_Listen 0.0.0.0 HTTP_Port 2022 diff --git a/tests/internal/data/reload/parsers.conf b/tests/internal/data/reload/parsers.conf new file mode 100644 index 00000000000..f7c3f9b3ed3 --- /dev/null +++ b/tests/internal/data/reload/parsers.conf @@ -0,0 +1,3 @@ +[PARSER] + Name reload_test + Format json diff --git a/tests/internal/reload.c b/tests/internal/reload.c index c56759f9eb9..f5018ed282a 100644 --- a/tests/internal/reload.c +++ b/tests/internal/reload.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ #define FLB_YAML FLB_TESTS_DATA_PATH "/data/reload/yaml/processor.yaml" #define FLB_YAML_MISSING_INCLUDE FLB_TESTS_DATA_PATH "/data/reload/yaml/missing_include.yaml" #define FLB_CLASSIC FLB_TESTS_DATA_PATH "/data/reload/fluent-bit.conf" +#define FLB_CLASSIC_PATH FLB_TESTS_DATA_PATH "/data/reload/" void test_reconstruct_cf() { @@ -147,11 +149,14 @@ void test_reload() cf = flb_cf_create_from_file(cf, FLB_CLASSIC); TEST_CHECK(cf != NULL); + ctx->config->conf_path = flb_strdup(FLB_CLASSIC_PATH); + TEST_CHECK(ctx->config->conf_path != NULL); ctx->config->conf_path_file = flb_sds_create(FLB_CLASSIC); ctx->config->enable_hot_reload = FLB_TRUE; status = flb_config_load_config_format(ctx->config, cf); TEST_CHECK(status == 0); + TEST_CHECK(flb_parser_get("reload_test", ctx->config) != NULL); /* Start the engine */ status = flb_start(ctx); @@ -162,12 +167,16 @@ void test_reload() status = flb_reload(ctx, cf_opts); TEST_CHECK(status == 0); + TEST_MSG("Expected reload status 0, got %d", status); sleep(2); /* flb context should be replaced with flb_reload() */ ctx = flb_context_get(); + TEST_CHECK(ctx->config->conf_path != NULL && + strcmp(ctx->config->conf_path, FLB_CLASSIC_PATH) == 0); + TEST_CHECK(flb_parser_get("reload_test", ctx->config) != NULL); TEST_CHECK(mk_list_size(&ctx->config->cf_opts->inputs) == 1); TEST_CHECK(mk_list_size(&ctx->config->inputs) == 2);