diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 06574c02..9043b311 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -32,11 +32,16 @@ jobs: submodules: recursive - name: Format run: make fmt - - name: Test + - name: Release Build Test env: CC: ${{ matrix.compiler }} CC_FOR_BUILD: ${{ matrix.compiler }} run: make run_test + - name: Asan Build Test + env: + CC: ${{ matrix.compiler }} + CC_FOR_BUILD: ${{ matrix.compiler }} + run: make run_test_asan - name: Prepare for release run: tar -zcf odyssey.linux-amd64.$(git rev-parse --short HEAD).tar.gz sources - name: Release diff --git a/CMakeLists.txt b/CMakeLists.txt index 921edf42..55883043 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,9 +62,9 @@ elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(CMAKE_C_FLAGS "-std=${CLANGCOMPILERSTANDART} -pedantic -Wall -Wextra -Wstrict-aliasing -g -O0 -pthread -D_GNU_SOURCE -DLDAP_DBG") set(OD_DEVEL_LVL 1) elseif("${CMAKE_BUILD_TYPE}" STREQUAL "ASAN") - set(CMAKE_C_FLAGS "-std=${CLANGCOMPILERSTANDART} -g -fsanitize=address,undefined -fno-sanitize-recover=all -pedantic -Wall -Wextra -Wstrict-aliasing -g -O0 -pthread -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "-std=${CLANGCOMPILERSTANDART} -g -fsanitize=address,undefined,leak -fno-sanitize-recover=all -fno-omit-frame-pointer -pedantic -Wall -Wextra -Wstrict-aliasing -g -O0 -pthread -D_GNU_SOURCE") elseif("${CMAKE_BUILD_TYPE}" STREQUAL "TSAN") - set(CMAKE_C_FLAGS "-std=${CLANGCOMPILERSTANDART} -g -fsanitize=thread -fno-sanitize-recover=all -pedantic -Wall -Wextra -Wstrict-aliasing -g -O0 -pthread -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "-std=${CLANGCOMPILERSTANDART} -g -fsanitize=thread -fno-sanitize-recover=all -fno-omit-frame-pointer -pedantic -Wall -Wextra -Wstrict-aliasing -g -O0 -pthread -D_GNU_SOURCE") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CC_FLAGS}") diff --git a/Makefile b/Makefile index 19cfe495..26adb1cb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -BUILD_TEST_DIR=build-dbg +BUILD_TEST_DIR=build BUILD_REL_DIR=build -BUILD_TEST_ASAN_DIR=build-asan +BUILD_TEST_ASAN_DIR=build ODY_DIR=$(PWD) TMP_BIN:=$(ODY_DIR)/tmp @@ -57,11 +57,7 @@ apply_fmt: build_asan: rm -rf $(BUILD_TEST_ASAN_DIR) mkdir -p $(BUILD_TEST_ASAN_DIR) - cd $(BUILD_TEST_ASAN_DIR) && $(CMAKE_BIN) -DCMAKE_BUILD_TYPE=ASAN $(ODY_DIR) && make -j$(CONCURRENCY) - -copy_asan_bin: - cp $(BUILD_TEST_ASAN_DIR)/sources/odyssey ./docker/bin/odyssey-asan - cp $(BUILD_TEST_ASAN_DIR)/test/odyssey_test ./docker/bin/odyssey_test_asan + cd $(BUILD_TEST_ASAN_DIR) && $(CMAKE_BIN) -DCMAKE_BUILD_TYPE=ASAN $(ODY_DIR) $(CMAKE_FLAGS) && make -j$(CONCURRENCY) build_release: rm -rf $(BUILD_REL_DIR) @@ -79,7 +75,11 @@ gdb: build_dbg run_test: # change dir, test would not work with absolute path ./cleanup-docker.sh - docker compose -f ./docker-compose-test.yml up --exit-code-from odyssey + ODYSSEY_TEST_BUILD_TYPE=build_release docker compose -f ./docker-compose-test.yml up --exit-code-from odyssey + +run_test_asan: + ./cleanup-docker.sh + ODYSSEY_TEST_BUILD_TYPE=build_asan docker compose -f ./docker-compose-test.yml up --exit-code-from odyssey submit-cov: mkdir cov-build && cd cov-build diff --git a/docker-compose-test.yml b/docker-compose-test.yml index fd557493..5812faa9 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -12,6 +12,8 @@ services: build: dockerfile: ./docker/Dockerfile context: . + args: + odyssey_build_type: "${ODYSSEY_TEST_BUILD_TYPE:-build_release}" environment: CMAKE_BUILD_TYPE: "${CMAKE_BUILD_TYPE:-Debug}" volumes: diff --git a/docker/Dockerfile b/docker/Dockerfile index b19e6b96..9caca74e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -16,13 +16,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ FROM odyssey-build-env AS odyssey-build +ARG odyssey_build_type + RUN mkdir build_dir COPY . /build_dir WORKDIR /build_dir -RUN make build_release -RUN make build_dbg -RUN make build_asan +RUN make ${odyssey_build_type} FROM golang:1.23-alpine AS golang-tests-builder @@ -70,6 +70,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ postgresql-server-dev-16 \ sudo \ git \ + vim \ docker-ce docker-ce-cli containerd.io RUN mkdir /tmp/odyssey @@ -88,13 +89,7 @@ COPY --from=golang-tests-builder /config-validation/pkg/config-validation /confi COPY --from=odyssey-build /build_dir/build/sources/odyssey /usr/bin/odyssey COPY --from=odyssey-build /build_dir/build/test/odyssey_test /usr/bin/odyssey_test -COPY --from=odyssey-build /build_dir/build-dbg/sources/odyssey /usr/bin/odyssey-dbg -COPY --from=odyssey-build /build_dir/build-dbg/test/odyssey_test /usr/bin/odyssey_test-dbg - -COPY --from=odyssey-build /build_dir/build-asan/sources/odyssey /usr/bin/odyssey-asan -COPY --from=odyssey-build /build_dir/build-asan/test/odyssey_test /usr/bin/odyssey_test_asan - -COPY --from=odyssey-build /build_dir/build-asan/test/machinarium /machinarium +COPY --from=odyssey-build /build_dir/build/test/machinarium /machinarium COPY ./docker/bin/ody-restart /usr/bin/ody-restart COPY ./docker/bin/ody-start /usr/bin/ody-start diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index cf44750c..4fee9ad9 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -4,7 +4,9 @@ set -ex /usr/bin/odyssey_test -/usr/bin/odyssey_test_asan +/usr/bin/odyssey /etc/odyssey/odyssey.conf +sleep 5 +ody-stop setup @@ -106,10 +108,6 @@ then fi echo "" > /var/log/odyssey.log -/usr/bin/odyssey-asan /etc/odyssey/odyssey.conf -sleep 5 -ody-stop - # TODO: rewrite #/shell-test/test.sh /shell-test/console_role_test.sh diff --git a/docker/lagpolling/test-lag.sh b/docker/lagpolling/test-lag.sh index 1b1f7f9b..502a310e 100755 --- a/docker/lagpolling/test-lag.sh +++ b/docker/lagpolling/test-lag.sh @@ -2,6 +2,7 @@ /usr/bin/odyssey /etc/odyssey/lag-conf.conf +sleep 3 for _ in $(seq 1 3); do PGPASSWORD=lolol psql -h localhost -p6432 -dpostgres -Uuser1 -c 'select 3' && exit 1 @@ -26,3 +27,5 @@ PGPASSWORD=lolol psql -h localhost -p6432 -dpostgres -Uuser1 -c 'select 3' || tr for _ in $(seq 1 3); do PGPASSWORD=lolol psql -h localhost -p6432 -dpostgres -Uuser1 -c 'select 3' || exit 1 done + +ody-stop diff --git a/docker/scram/test_scram.sh b/docker/scram/test_scram.sh index dd2a6296..cbe99437 100755 --- a/docker/scram/test_scram.sh +++ b/docker/scram/test_scram.sh @@ -1,6 +1,7 @@ #!/bin/bash -x /usr/bin/odyssey /scram/config.conf +sleep 3 /scram/test_scram_backend.sh if [ $? -eq 1 ] diff --git a/sources/extension.h b/sources/extension.h index fe16e59c..7a8c0269 100644 --- a/sources/extension.h +++ b/sources/extension.h @@ -12,6 +12,10 @@ struct od_extension { static inline od_retcode_t od_extensions_init(od_extension_t *extensions) { extensions->modules = malloc(sizeof(od_module_t)); + if (extensions->modules == NULL) { + return 1; + } + od_modules_init(extensions->modules); return OK_RESPONSE; diff --git a/sources/instance.c b/sources/instance.c index 32674a3d..7cd53704 100644 --- a/sources/instance.c +++ b/sources/instance.c @@ -61,7 +61,11 @@ void od_config_testing(od_instance_t *instance) od_error_init(&error); od_router_init(&router, &global); od_hba_init(&hba); - od_extensions_init(&extensions); + if (od_extensions_init(&extensions) != 0) { + od_error(&instance->logger, "config", NULL, NULL, + "failed to init extensions"); + goto error; + }; int rc; rc = od_config_reader_import(&instance->config, &router.rules, &error, @@ -144,7 +148,13 @@ int od_instance_main(od_instance_t *instance, int argc, char **argv) od_router_init(&router, &global); od_cron_init(&cron); od_worker_pool_init(&worker_pool); - od_extensions_init(&extensions); + + if (od_extensions_init(&extensions) != 0) { + od_error(&instance->logger, "config", NULL, NULL, + "failed to extensions init"); + goto error; + } + od_hba_init(&hba); od_global_init(&global, instance, &system, &router, &cron, &worker_pool, &extensions, &hba); diff --git a/sources/sighandler.c b/sources/sighandler.c index 032f5909..b4d8f6fe 100644 --- a/sources/sighandler.c +++ b/sources/sighandler.c @@ -58,11 +58,11 @@ od_attribute_noreturn() void od_system_shutdown(od_system_t *system, /* Prevent OpenSSL usage during deinitialization */ od_worker_pool_wait(); + od_extension_free(&instance->logger, system->global->extensions); + #ifdef OD_SYSTEM_SHUTDOWN_CLEANUP od_router_free(system->global->router); - od_extension_free(&instance->logger, system->global->extensions); - od_system_cleanup(system); /* stop machinaruim and free */ diff --git a/sources/storage.c b/sources/storage.c index 4173c5e4..444f3c9c 100644 --- a/sources/storage.c +++ b/sources/storage.c @@ -17,7 +17,6 @@ od_storage_watchdog_t *od_storage_watchdog_allocate(od_global_t *global) return NULL; } memset(watchdog, 0, sizeof(od_storage_watchdog_t)); - watchdog->check_retry = 10; watchdog->global = global; watchdog->online = 1; pthread_mutex_init(&watchdog->mu, NULL); @@ -25,8 +24,7 @@ od_storage_watchdog_t *od_storage_watchdog_allocate(od_global_t *global) return watchdog; } -static inline int -od_storage_watchdog_online_status(od_storage_watchdog_t *watchdog) +static inline int od_storage_watchdog_is_online(od_storage_watchdog_t *watchdog) { int ret; pthread_mutex_lock(&watchdog->mu); @@ -195,6 +193,16 @@ od_rule_storage_t *od_rules_storage_copy(od_rule_storage_t *storage) return NULL; } +static inline int strtol_safe(const char *s, int len) +{ + const int buff_len = 32; + char buff[buff_len]; + memset(buff, 0, sizeof(buff)); + memcpy(buff, s, len); + + return strtol(buff, NULL, 0); +} + static inline int od_storage_watchdog_parse_lag_from_datarow(machine_msg_t *msg, int *repl_lag) { @@ -217,14 +225,13 @@ static inline int od_storage_watchdog_parse_lag_from_datarow(machine_msg_t *msg, if (count != 1) goto error; - /* (not used) */ uint32_t lag_len; rc = kiwi_read32(&lag_len, &pos, &pos_size); if (kiwi_unlikely(rc == -1)) { goto error; } - *repl_lag = strtol(pos, NULL, 0); + *repl_lag = strtol_safe(pos, (int)lag_len); return OK_RESPONSE; error: @@ -239,24 +246,20 @@ static inline int od_router_update_heartbeat_cb(od_route_t *route, void **argv) return 0; } -void od_storage_watchdog_watch(void *arg) +static inline od_client_t * +od_storage_watchdog_prepare_client(od_storage_watchdog_t *watchdog) { - od_storage_watchdog_t *watchdog = (od_storage_watchdog_t *)arg; od_global_t *global = watchdog->global; od_router_t *router = global->router; od_instance_t *instance = global->instance; - od_debug(&instance->logger, "watchdog", NULL, NULL, - "start lag polling watchdog "); - - /* create internal auth client */ od_client_t *watchdog_client; watchdog_client = od_client_allocate_internal(global, "storage-watchdog"); if (watchdog_client == NULL) { od_error(&instance->logger, "watchdog", NULL, NULL, "route storage watchdog failed to allocate client"); - return; + return NULL; } watchdog_client->is_watchdog = true; @@ -271,11 +274,6 @@ void od_storage_watchdog_watch(void *arg) kiwi_var_set(&watchdog_client->startup.database, KIWI_VAR_UNDEF, watchdog->route_db, strlen(watchdog->route_db) + 1); - machine_msg_t *msg; - - int last_heartbeat = 0; - int rc; - /* route */ od_router_status_t status; status = od_router_route(router, watchdog_client); od_debug(&instance->logger, "watchdog", watchdog_client, NULL, @@ -286,92 +284,161 @@ void od_storage_watchdog_watch(void *arg) od_error(&instance->logger, "watchdog", watchdog_client, NULL, "route storage watchdog failed: %s", od_router_status_to_str(status)); - return; + + od_client_free(watchdog_client); + + return NULL; } - for (;;) { - /* attach client to some route */ - status = od_router_attach(router, watchdog_client, false); - od_debug(&instance->logger, "watchdog", watchdog_client, NULL, - "attaching wd client to backend connection status: %s", - od_router_status_to_str(status)); + return watchdog_client; +} - if (status != OD_ROUTER_OK) { - /* 1 second soft interval */ - machine_sleep(1000); - continue; - } - od_server_t *server; - server = watchdog_client->server; - od_debug(&instance->logger, "watchdog", watchdog_client, server, - "attached to server %s%.*s", server->id.id_prefix, - (int)sizeof(server->id.id), server->id.id); - - /* connect to server, if necessary */ - if (server->io.io == NULL) { - rc = od_backend_connect(server, "watchdog", NULL, - watchdog_client); - if (rc == NOT_OK_RESPONSE) { - od_debug( - &instance->logger, "watchdog", - watchdog_client, server, - "backend connect failed, retry after 1 sec"); - od_router_close(router, watchdog_client); - /* 1 second soft interval */ - machine_sleep(1000); - continue; - } - } +static inline void +od_storage_watchdog_close_client(od_storage_watchdog_t *watchdog, + od_client_t *watchdog_client) +{ + od_global_t *global = watchdog->global; + od_router_t *router = global->router; - for (int retry = 0; retry < watchdog->check_retry; ++retry) { - char *qry = watchdog->query; - - msg = od_query_do(server, "watchdog", qry, NULL); - if (msg != NULL) { - rc = od_storage_watchdog_parse_lag_from_datarow( - msg, &last_heartbeat); - machine_msg_free(msg); - od_router_close(router, watchdog_client); - } else { - od_debug( - &instance->logger, "watchdog", - watchdog_client, server, - "receive msg failed, closing backend connection"); - rc = NOT_OK_RESPONSE; - od_router_close(router, watchdog_client); - break; - } + od_router_close(router, watchdog_client); + od_router_unroute(router, watchdog_client); + od_client_free(watchdog_client); +} - if (rc == OK_RESPONSE) { - od_debug( - &instance->logger, "watchdog", - watchdog_client, server, - "send heartbeat arenda update to routes with value %d", - last_heartbeat); - void *argv[] = { &last_heartbeat }; - od_router_foreach(router, - od_router_update_heartbeat_cb, - argv); - break; - } - // retry - } +static inline od_client_t * +od_storage_create_and_connect_watchdog_client(od_storage_watchdog_t *watchdog) +{ + od_global_t *global = watchdog->global; + od_router_t *router = global->router; + od_instance_t *instance = global->instance; - /* detach and unroute */ - if (watchdog_client->server) { - od_router_detach(router, watchdog_client); - } + od_client_t *watchdog_client; + watchdog_client = od_storage_watchdog_prepare_client(watchdog); + if (watchdog_client == NULL) { + od_error(&instance->logger, "watchdog", NULL, NULL, + "route storage watchdog failed to prepare client"); - if (!od_storage_watchdog_online_status(watchdog)) { + return NULL; + } + + od_router_status_t status; + status = od_router_attach(router, watchdog_client, false); + od_debug(&instance->logger, "watchdog", watchdog_client, NULL, + "attaching wd client to backend connection status: %s", + od_router_status_to_str(status)); + if (status != OD_ROUTER_OK) { + od_error(&instance->logger, "watchdog", watchdog_client, NULL, + "can't attach wd client to backend connection: %s", + od_router_status_to_str(status)); + + od_client_free(watchdog_client); + + return NULL; + } + + od_server_t *server; + server = watchdog_client->server; + od_debug(&instance->logger, "watchdog", watchdog_client, server, + "attached to server %s%.*s", server->id.id_prefix, + (int)sizeof(server->id.id), server->id.id); + + /* connect to server, if necessary */ + if (server->io.io == NULL) { + int rc; + rc = od_backend_connect(server, "watchdog", NULL, + watchdog_client); + if (rc == NOT_OK_RESPONSE) { od_debug(&instance->logger, "watchdog", watchdog_client, - NULL, - "deallocating obsolete storage watchdog"); - od_client_free(watchdog_client); - od_storage_watchdog_free(watchdog); - return; + server, "backend connect failed"); + + od_storage_watchdog_close_client(watchdog, + watchdog_client); + + return NULL; } + } + + return watchdog_client; +} + +static inline void od_storage_update_route_last_heartbeats( + od_instance_t *instance, od_client_t *watchdog_client, + od_server_t *server, od_router_t *router, int last_heartbeat) +{ + od_log(&instance->logger, "watchdog", watchdog_client, server, + "send heartbeat arenda update to routes with value %d", + last_heartbeat); + + void *argv[] = { &last_heartbeat }; + + od_router_foreach(router, od_router_update_heartbeat_cb, argv); +} + +static inline void +od_storage_watchdog_do_polling_step(od_storage_watchdog_t *watchdog) +{ + od_global_t *global = watchdog->global; + od_router_t *router = global->router; + od_instance_t *instance = global->instance; + + od_client_t *watchdog_client; + watchdog_client = + od_storage_create_and_connect_watchdog_client(watchdog); + if (watchdog_client == NULL) { + return; + } + + od_server_t *server; + server = watchdog_client->server; + + machine_msg_t *msg; + msg = od_query_do(server, "watchdog", watchdog->query, NULL); + if (msg == NULL) { + od_error(&instance->logger, "watchdog", watchdog_client, server, + "receive msg failed, closing backend connection"); + + od_storage_watchdog_close_client(watchdog, watchdog_client); + return; + } + + int last_heartbeat; + if (od_storage_watchdog_parse_lag_from_datarow(msg, &last_heartbeat) == + 0) { + od_storage_update_route_last_heartbeats(instance, + watchdog_client, server, + router, last_heartbeat); + } else { + od_error(&instance->logger, "watchdog", watchdog_client, server, + "can't parse lag"); + } + + machine_msg_free(msg); + + od_storage_watchdog_close_client(watchdog, watchdog_client); +} + +static inline void +od_storage_watchdog_do_polling_loop(od_storage_watchdog_t *watchdog) +{ + while (od_storage_watchdog_is_online(watchdog)) { + od_storage_watchdog_do_polling_step(watchdog); - /* 1 second soft interval */ machine_sleep(1000); } } + +void od_storage_watchdog_watch(void *arg) +{ + od_storage_watchdog_t *watchdog = (od_storage_watchdog_t *)arg; + od_global_t *global = watchdog->global; + od_instance_t *instance = global->instance; + + od_debug(&instance->logger, "watchdog", NULL, NULL, + "start lag polling watchdog"); + + od_storage_watchdog_do_polling_loop(watchdog); + + od_debug(&instance->logger, "watchdog", NULL, NULL, + "deallocating storage watchdog"); + od_storage_watchdog_free(watchdog); +} diff --git a/sources/storage.h b/sources/storage.h index 677186a4..4916489d 100644 --- a/sources/storage.h +++ b/sources/storage.h @@ -25,7 +25,6 @@ struct od_storage_watchdog { char *query; int interval; - int check_retry; /* soft shutdown on reload */ pthread_mutex_t mu;