From 4faddf18ca8ca3adb93cf1e4e620be9eaf0f6bf4 Mon Sep 17 00:00:00 2001 From: Oran Agra Date: Mon, 22 Aug 2022 15:53:56 +0800 Subject: [PATCH] Build TLS as a loadable module * Support BUILD_TLS=module to be loaded as a module via config file or command line. e.g. redis-server --loadmodule redis-tls.so * Updates to redismodule.h to allow it to be used side by side with server.h by defining REDISMODULE_CORE_MODULE * Changes to server.h, redismodule.h and module.c to avoid repeated type declarations (gcc 4.8 doesn't like these) * Add a mechanism for non-ABI neutral modules (ones who include server.h) to refuse loading if they detect not being built together with redis (release.c) * Fix wrong signature of RedisModuleDefragFunc, this could break compilation of a module, but not the ABI * Move initialization of listeners in server.c to be after loading the modules * Config TLS after initialization of listeners * Init cluster after initialization of listeners * Add TLS module to CI * Fix a test suite race conditions: Now that the listeners are initialized later, it's not sufficient to wait for the PID message in the log, we need to wait for the "Server Initialized" message. * Fix issues with moduleconfigs test as a result from start_server waiting for "Server Initialized" * Fix issues with modules/infra test as a result of an additional module present Notes about Sentinel: Sentinel can't really rely on the tls module, since it uses hiredis to initiate connections and depends on OpenSSL (won't be able to use any other connection modules for that), so it was decided that when TLS is built as a module, sentinel does not support TLS at all. This means that it keeps using redis_tls_ctx and redis_tls_client_ctx directly. Example code of config in redis-tls.so(may be use in the future): RedisModuleString *tls_cfg = NULL; void tlsInfo(RedisModuleInfoCtx *ctx, int for_crash_report) { UNUSED(for_crash_report); RedisModule_InfoAddSection(ctx, ""); RedisModule_InfoAddFieldLongLong(ctx, "var", 42); } int tlsCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); return RedisModule_ReplyWithString(ctx, argv[1]); } RedisModuleString *getStringConfigCommand(const char *name, void *privdata) { REDISMODULE_NOT_USED(name); REDISMODULE_NOT_USED(privdata); return tls_cfg; } int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { REDISMODULE_NOT_USED(name); REDISMODULE_NOT_USED(err); REDISMODULE_NOT_USED(privdata); if (tls_cfg) RedisModule_FreeString(NULL, tls_cfg); RedisModule_RetainString(NULL, new); tls_cfg = new; return REDISMODULE_OK; } int RedisModule_OnLoad(void *ctx, RedisModuleString **argv, int argc) { .... if (RedisModule_CreateCommand(ctx,"tls",tlsCommand,"",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_RegisterStringConfig(ctx, "cfg", "", REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { if (tls_cfg) { RedisModule_FreeString(ctx, tls_cfg); tls_cfg = NULL; } return REDISMODULE_ERR; } ... } Co-authored-by: zhenwei pi Signed-off-by: zhenwei pi --- .github/workflows/ci.yml | 3 +- .github/workflows/daily.yml | 12 +- TLS.md | 17 +++ deps/Makefile | 2 +- src/Makefile | 39 +++++-- src/connection.h | 13 +-- src/mkreleasehdr.sh | 2 + src/module.c | 8 +- src/redismodule.h | 44 ++++--- src/release.c | 7 +- src/sentinel.c | 16 +-- src/server.c | 152 +++++++++++++------------ src/server.h | 25 ++-- src/tls.c | 62 +++++++--- tests/instances.tcl | 11 +- tests/modules/defragtest.c | 4 +- tests/support/server.tcl | 5 +- tests/support/util.tcl | 22 ++++ tests/test_helper.tcl | 7 +- tests/unit/moduleapi/infra.tcl | 9 +- tests/unit/moduleapi/moduleconfigs.tcl | 40 ++----- 21 files changed, 302 insertions(+), 198 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b406f74b..f6a61b0e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,8 @@ jobs: steps: - uses: actions/checkout@v3 - name: make - run: make SANITIZER=address REDIS_CFLAGS='-Werror' + # build with TLS module just for compilation coverage + run: make SANITIZER=address REDIS_CFLAGS='-Werror' BUILD_TLS=module - name: testprep run: sudo apt-get install tcl8.6 tclx -y - name: test diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index da3c8a21f..e9e0549af 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -539,7 +539,7 @@ jobs: run: | yum -y install centos-release-scl epel-release yum -y install devtoolset-7 openssl-devel openssl - scl enable devtoolset-7 "make BUILD_TLS=yes REDIS_CFLAGS='-Werror'" + scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'" - name: testprep run: | yum -y install tcl tcltls tclx @@ -547,19 +547,19 @@ jobs: - name: test if: true && !contains(github.event.inputs.skiptests, 'redis') run: | - ./runtest --accurate --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}} + ./runtest --accurate --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}} - name: module api test if: true && !contains(github.event.inputs.skiptests, 'modules') run: | - ./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}} + ./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}} - name: sentinel tests if: true && !contains(github.event.inputs.skiptests, 'sentinel') run: | - ./runtest-sentinel --tls ${{github.event.inputs.cluster_test_args}} + ./runtest-sentinel ${{github.event.inputs.cluster_test_args}} - name: cluster tests if: true && !contains(github.event.inputs.skiptests, 'cluster') run: | - ./runtest-cluster --tls ${{github.event.inputs.cluster_test_args}} + ./runtest-cluster --tls-module ${{github.event.inputs.cluster_test_args}} test-centos7-tls-no-tls: runs-on: ubuntu-latest @@ -582,7 +582,7 @@ jobs: run: | yum -y install centos-release-scl epel-release yum -y install devtoolset-7 openssl-devel openssl - scl enable devtoolset-7 "make BUILD_TLS=yes REDIS_CFLAGS='-Werror'" + scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'" - name: testprep run: | yum -y install tcl tcltls tclx diff --git a/TLS.md b/TLS.md index 2d020d0ce..b9bce7edc 100644 --- a/TLS.md +++ b/TLS.md @@ -9,8 +9,14 @@ Getting Started To build with TLS support you'll need OpenSSL development libraries (e.g. libssl-dev on Debian/Ubuntu). +To build TLS support as Redis built-in: Run `make BUILD_TLS=yes`. +Or to build TLS as Redis module: +Run `make BUILD_TLS=module`. + +Note that sentinel mode does not support TLS module. + ### Tests To run Redis test suite with TLS, you'll need TLS support for TCL (i.e. @@ -22,16 +28,27 @@ To run Redis test suite with TLS, you'll need TLS support for TCL (i.e. 2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis Cluster tests in TLS mode. +3. Run `./runtest --tls-module` or `./runtest-cluster --tls-module` to + run Redis and Redis cluster tests in TLS mode with Redis module. + ### Running manually To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was invoked so sample certificates/keys are available): +For TLS built-in mode: ./src/redis-server --tls-port 6379 --port 0 \ --tls-cert-file ./tests/tls/redis.crt \ --tls-key-file ./tests/tls/redis.key \ --tls-ca-cert-file ./tests/tls/ca.crt +For TLS module mode: + ./src/redis-server --tls-port 6379 --port 0 \ + --tls-cert-file ./tests/tls/redis.crt \ + --tls-key-file ./tests/tls/redis.key \ + --tls-ca-cert-file ./tests/tls/ca.crt \ + --loadmodule src/redis-tls.so + To connect to this Redis server with `redis-cli`: ./src/redis-cli --tls \ diff --git a/deps/Makefile b/deps/Makefile index 8592e1766..96dbb8c1d 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -44,7 +44,7 @@ distclean: .PHONY: distclean -ifeq ($(BUILD_TLS),yes) +ifneq (,$(filter $(BUILD_TLS),yes module)) HIREDIS_MAKE_FLAGS = USE_SSL=1 endif diff --git a/src/Makefile b/src/Makefile index 814f3c0aa..fdfef2b3c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -267,24 +267,41 @@ ifeq ($(MALLOC),jemalloc) FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS) endif -ifeq ($(BUILD_TLS),yes) - FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS) - FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS) - LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?) +# LIBSSL & LIBCRYPTO +LIBSSL_LIBS= +LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?) ifeq ($(LIBSSL_PKGCONFIG),0) LIBSSL_LIBS=$(shell $(PKG_CONFIG) --libs libssl) else LIBSSL_LIBS=-lssl endif - LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?) +LIBCRYPTO_LIBS= +LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?) ifeq ($(LIBCRYPTO_PKGCONFIG),0) LIBCRYPTO_LIBS=$(shell $(PKG_CONFIG) --libs libcrypto) else LIBCRYPTO_LIBS=-lcrypto endif + +BUILD_NO:=0 +BUILD_YES:=1 +BUILD_MODULE:=2 +ifeq ($(BUILD_TLS),yes) + FINAL_CFLAGS+=-DUSE_OPENSSL=$(BUILD_YES) $(OPENSSL_CFLAGS) -DBUILD_TLS_MODULE=$(BUILD_NO) + FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS) FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) endif +TLS_MODULE= +TLS_MODULE_NAME:=redis-tls$(PROG_SUFFIX).so +TLS_MODULE_CFLAGS:=$(FINAL_CFLAGS) +ifeq ($(BUILD_TLS),module) + FINAL_CFLAGS+=-DUSE_OPENSSL=$(BUILD_MODULE) $(OPENSSL_CFLAGS) + TLS_CLIENT_LIBS = ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) + TLS_MODULE=$(TLS_MODULE_NAME) + TLS_MODULE_CFLAGS+=-DUSE_OPENSSL=$(BUILD_MODULE) $(OPENSSL_CFLAGS) -DBUILD_TLS_MODULE=$(BUILD_MODULE) +endif + ifndef V define MAKE_INSTALL @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$(1)$(ENDCOLOR) 1>&2 @@ -325,7 +342,7 @@ REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX) REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX) ALL_SOURCES=$(sort $(patsubst %.o,%.c,$(REDIS_SERVER_OBJ) $(REDIS_CLI_OBJ) $(REDIS_BENCHMARK_OBJ))) -all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) +all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) $(TLS_MODULE) @echo "" @echo "Hint: It's a good idea to run 'make test' ;)" @echo "" @@ -385,13 +402,17 @@ $(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) +# redis-tls.so +$(TLS_MODULE_NAME): $(REDIS_SERVER_NAME) + $(QUIET_CC)$(CC) -o $@ tls.c -shared -fPIC $(TLS_MODULE_CFLAGS) $(TLS_CLIENT_LIBS) + # redis-cli $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) - $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) + $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) $(TLS_CLIENT_LIBS) # redis-benchmark $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) - $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS) + $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS) $(TLS_CLIENT_LIBS) DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d) -include $(DEP) @@ -410,7 +431,7 @@ commands.c: commands/*.json ../utils/generate-command-code.py endif clean: - rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep + rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep *.so rm -f $(DEP) .PHONY: clean diff --git a/src/connection.h b/src/connection.h index 9fe5277e5..61ca205fe 100644 --- a/src/connection.h +++ b/src/connection.h @@ -107,8 +107,6 @@ typedef struct ConnectionType { /* TLS specified methods */ sds (*get_peer_cert)(struct connection *conn); - void* (*get_ctx)(void); - void* (*get_client_ctx)(void); } ConnectionType; struct connection { @@ -355,15 +353,6 @@ int connKeepAlive(connection *conn, int interval); int connSendTimeout(connection *conn, long long ms); int connRecvTimeout(connection *conn, long long ms); -/* Helpers for tls special considerations */ -static inline void *connTypeGetCtx(ConnectionType *ct) { - return ct->get_ctx(); -} - -static inline void *connTypeGetClientCtx(ConnectionType *ct) { - return ct->get_client_ctx(); -} - /* Get cert for the secure connection */ static inline sds connGetPeerCert(connection *conn) { if (conn->type->get_peer_cert) { @@ -399,7 +388,7 @@ static inline connection *connCreate(ConnectionType *ct) { return ct->conn_create(); } -/* Create a accepted connection of specified type. +/* Create an accepted connection of specified type. * priv is connection type specified argument */ static inline connection *connCreateAccepted(ConnectionType *ct, int fd, void *priv) { return ct->conn_create_accepted(fd, priv); diff --git a/src/mkreleasehdr.sh b/src/mkreleasehdr.sh index 236c26c2b..117b9e86f 100755 --- a/src/mkreleasehdr.sh +++ b/src/mkreleasehdr.sh @@ -11,4 +11,6 @@ test -f release.h || touch release.h echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h echo "#define REDIS_BUILD_ID \"$BUILD_ID\"" >> release.h +echo "#include \"version.h\"" >> release.h +echo "#define REDIS_BUILD_ID_RAW REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1" >> release.h touch release.c # Force recompile of release.c diff --git a/src/module.c b/src/module.c index 7644e2c77..f0f49837b 100644 --- a/src/module.c +++ b/src/module.c @@ -69,14 +69,14 @@ * pointers that have an API the module can call with them) * -------------------------------------------------------------------------- */ -typedef struct RedisModuleInfoCtx { +struct RedisModuleInfoCtx { struct RedisModule *module; dict *requested_sections; sds info; /* info string we collected so far */ int sections; /* number of sections we collected so far */ int in_section; /* indication if we're in an active section or not */ int in_dict_field; /* indication that we're currently appending to a dict */ -} RedisModuleInfoCtx; +}; /* This represents a shared API. Shared APIs will be used to populate * the server.sharedapi dictionary, mapping names of APIs exported by @@ -12211,13 +12211,13 @@ const char *RM_GetCurrentCommandName(RedisModuleCtx *ctx) { /* The defrag context, used to manage state during calls to the data type * defrag callback. */ -typedef struct RedisModuleDefragCtx { +struct RedisModuleDefragCtx { long defragged; long long int endtime; unsigned long *cursor; struct redisObject *key; /* Optional name of key processed, NULL when unknown. */ int dbid; /* The dbid of the key being processed, -1 when unknown. */ -} RedisModuleDefragCtx; +}; /* Register a defrag callback for global data, i.e. anything that the module * may allocate that is not tied to a specific data type. diff --git a/src/redismodule.h b/src/redismodule.h index 6397e704e..36e8bf51f 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -750,13 +750,41 @@ typedef enum { REDISMODULE_ACL_LOG_CHANNEL /* Channel authorization failure */ } RedisModuleACLLogEntryReason; +/* Incomplete structures needed by both the core and modules. */ +typedef struct RedisModuleString RedisModuleString; +typedef struct RedisModuleIO RedisModuleIO; +typedef struct RedisModuleDigest RedisModuleDigest; +typedef struct RedisModuleInfoCtx RedisModuleInfoCtx; +typedef struct RedisModuleDefragCtx RedisModuleDefragCtx; + +/* Function pointers needed by both the core and modules, these needs to be + * exposed since you can't cast a function pointer to (void *). */ +typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report); +typedef void (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx); +typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata); + /* ------------------------- End of common defines ------------------------ */ -#ifndef REDISMODULE_CORE +#if defined REDISMODULE_CORE +/* Things only defined for the modules core (server), not exported to modules + * that include this file. */ + +#define RedisModuleString robj + +#endif /* defined REDISMODULE_CORE */ + +#if !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE +/* Things defined for modules, but not for core-modules. */ typedef long long mstime_t; typedef long long ustime_t; +#endif /* !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE */ + +/* ----------- The rest of the defines are only for modules ----------------- */ +#if !defined REDISMODULE_CORE || defined REDISMODULE_CORE_MODULE +/* Things defined for modules and core-modules. */ + /* Macro definitions specific to individual compilers */ #ifndef REDISMODULE_ATTR_UNUSED # ifdef __GNUC__ @@ -786,21 +814,16 @@ typedef long long ustime_t; typedef struct RedisModuleCtx RedisModuleCtx; typedef struct RedisModuleCommand RedisModuleCommand; typedef struct RedisModuleKey RedisModuleKey; -typedef struct RedisModuleString RedisModuleString; typedef struct RedisModuleCallReply RedisModuleCallReply; -typedef struct RedisModuleIO RedisModuleIO; typedef struct RedisModuleType RedisModuleType; -typedef struct RedisModuleDigest RedisModuleDigest; typedef struct RedisModuleBlockedClient RedisModuleBlockedClient; typedef struct RedisModuleClusterInfo RedisModuleClusterInfo; typedef struct RedisModuleDict RedisModuleDict; typedef struct RedisModuleDictIter RedisModuleDictIter; typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx; typedef struct RedisModuleCommandFilter RedisModuleCommandFilter; -typedef struct RedisModuleInfoCtx RedisModuleInfoCtx; typedef struct RedisModuleServerInfoData RedisModuleServerInfoData; typedef struct RedisModuleScanCursor RedisModuleScanCursor; -typedef struct RedisModuleDefragCtx RedisModuleDefragCtx; typedef struct RedisModuleUser RedisModuleUser; typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx; @@ -827,11 +850,8 @@ typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const cha typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data); typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter); typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data); -typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report); typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata); typedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata); -typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata); -typedef int (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx); typedef RedisModuleString * (*RedisModuleConfigGetStringFunc)(const char *name, void *privdata); typedef long long (*RedisModuleConfigGetNumericFunc)(const char *name, void *privdata); typedef int (*RedisModuleConfigGetBoolFunc)(const char *name, void *privdata); @@ -1559,11 +1579,5 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int #define RMAPI_FUNC_SUPPORTED(func) (func != NULL) -#else - -/* Things only defined for the modules core, not exported to modules - * including this file. */ -#define RedisModuleString robj - #endif /* REDISMODULE_CORE */ #endif /* REDISMODULE_H */ diff --git a/src/release.c b/src/release.c index e0bd018fc..adc7e55dd 100644 --- a/src/release.c +++ b/src/release.c @@ -35,7 +35,6 @@ #include #include "release.h" -#include "version.h" #include "crc64.h" char *redisGitSHA1(void) { @@ -46,8 +45,12 @@ char *redisGitDirty(void) { return REDIS_GIT_DIRTY; } +const char *redisBuildIdRaw(void) { + return REDIS_BUILD_ID_RAW; +} + uint64_t redisBuildId(void) { - char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1; + char *buildid = REDIS_BUILD_ID_RAW; return crc64(0,(unsigned char*)buildid,strlen(buildid)); } diff --git a/src/sentinel.c b/src/sentinel.c index 881f4b909..a75eb3236 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -30,7 +30,7 @@ #include "server.h" #include "hiredis.h" -#ifdef USE_OPENSSL +#if USE_OPENSSL == 1 /* BUILD_YES */ #include "openssl/ssl.h" #include "hiredis_ssl.h" #endif @@ -44,6 +44,11 @@ extern char **environ; +#if USE_OPENSSL == 1 /* BUILD_YES */ +extern SSL_CTX *redis_tls_ctx; +extern SSL_CTX *redis_tls_client_ctx; +#endif + #define REDIS_SENTINEL_PORT 26379 /* ======================== Sentinel global state =========================== */ @@ -2373,12 +2378,7 @@ void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char } static int instanceLinkNegotiateTLS(redisAsyncContext *context) { -#ifndef USE_OPENSSL - (void) context; -#else - SSL_CTX *redis_tls_ctx = connTypeGetCtx(connectionTypeTls()); - SSL_CTX *redis_tls_client_ctx = connTypeGetClientCtx(connectionTypeTls()); - +#if USE_OPENSSL == 1 /* BUILD_YES */ if (!redis_tls_ctx) return C_ERR; SSL *ssl = SSL_new(redis_tls_client_ctx ? redis_tls_client_ctx : redis_tls_ctx); if (!ssl) return C_ERR; @@ -2387,6 +2387,8 @@ static int instanceLinkNegotiateTLS(redisAsyncContext *context) { SSL_free(ssl); return C_ERR; } +#else + UNUSED(context); #endif return C_OK; } diff --git a/src/server.c b/src/server.c index f01805840..b6a72ae88 100644 --- a/src/server.c +++ b/src/server.c @@ -2446,12 +2446,6 @@ void initServer(void) { exit(1); } - if ((server.tls_port || server.tls_replication || server.tls_cluster) - && connTypeConfigure(connectionTypeTls(), &server.tls_ctx_config, 1) == C_ERR) { - serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info."); - exit(1); - } - for (j = 0; j < CLIENT_MEM_USAGE_BUCKETS; j++) { server.client_mem_usage_buckets[j].mem_usage_sum = 0; server.client_mem_usage_buckets[j].clients = listCreate(); @@ -2470,40 +2464,6 @@ void initServer(void) { } server.db = zmalloc(sizeof(redisDb)*server.dbnum); - /* Setup listeners from server config for TCP/TLS/Unix */ - int conn_index; - connListener *listener; - if (server.port != 0) { - conn_index = connectionIndexByType(CONN_TYPE_SOCKET); - if (conn_index < 0) - serverPanic("Failed finding connection listener of %s", CONN_TYPE_SOCKET); - listener = &server.listeners[conn_index]; - listener->bindaddr = server.bindaddr; - listener->bindaddr_count = server.bindaddr_count; - listener->port = server.port; - listener->ct = connectionByType(CONN_TYPE_SOCKET); - } - if (server.tls_port != 0) { - conn_index = connectionIndexByType(CONN_TYPE_TLS); - if (conn_index < 0) - serverPanic("Failed finding connection listener of %s", CONN_TYPE_TLS); - listener = &server.listeners[conn_index]; - listener->bindaddr = server.bindaddr; - listener->bindaddr_count = server.bindaddr_count; - listener->port = server.tls_port; - listener->ct = connectionByType(CONN_TYPE_TLS); - } - if (server.unixsocket != NULL) { - conn_index = connectionIndexByType(CONN_TYPE_UNIX); - if (conn_index < 0) - serverPanic("Failed finding connection listener of %s", CONN_TYPE_UNIX); - listener = &server.listeners[conn_index]; - listener->bindaddr = &server.unixsocket; - listener->bindaddr_count = 1; - listener->ct = connectionByType(CONN_TYPE_UNIX); - listener->priv = &server.unixsocketperm; /* Unix socket specified */ - } - /* Create the Redis databases, and initialize other internal state. */ for (j = 0; j < server.dbnum; j++) { server.db[j].dict = dictCreate(&dbDictType); @@ -2584,29 +2544,6 @@ void initServer(void) { exit(1); } - /* create all the configured listener, and add handler to start to accept */ - int listen_fds = 0; - for (j = 0; j < CONN_TYPE_MAX; j++) { - listener = &server.listeners[j]; - if (listener->ct == NULL) - continue; - - if (connListen(listener) == C_ERR) { - serverLog(LL_WARNING, "Failed listening on port %u (%s), aborting.", listener->port, listener->ct->get_type(NULL)); - exit(1); - } - - if (createSocketAcceptHandler(listener, connAcceptHandler(listener->ct)) != C_OK) - serverPanic("Unrecoverable error creating %s listener accept handler.", listener->ct->get_type(NULL)); - - listen_fds += listener->count; - } - - if (listen_fds == 0) { - serverLog(LL_WARNING, "Configured to not listen anywhere, exiting."); - exit(1); - } - /* Register a readable event for the pipe used to awake the event loop * from module threads. */ if (aeCreateFileEvent(server.el, server.module_pipe[0], AE_READABLE, @@ -2630,7 +2567,6 @@ void initServer(void) { server.maxmemory_policy = MAXMEMORY_NO_EVICTION; } - if (server.cluster_enabled) clusterInit(); scriptingInit(1); functionsInit(); slowlogInit(); @@ -2642,6 +2578,78 @@ void initServer(void) { applyWatchdogPeriod(); } +void initListeners() { + /* Setup listeners from server config for TCP/TLS/Unix */ + int conn_index; + connListener *listener; + if (server.port != 0) { + conn_index = connectionIndexByType(CONN_TYPE_SOCKET); + if (conn_index < 0) + serverPanic("Failed finding connection listener of %s", CONN_TYPE_SOCKET); + listener = &server.listeners[conn_index]; + listener->bindaddr = server.bindaddr; + listener->bindaddr_count = server.bindaddr_count; + listener->port = server.port; + listener->ct = connectionByType(CONN_TYPE_SOCKET); + } + + if (server.tls_port || server.tls_replication || server.tls_cluster) { + ConnectionType *ct_tls = connectionTypeTls(); + if (!ct_tls) { + serverLog(LL_WARNING, "Failed finding TLS support."); + exit(1); + } + if (connTypeConfigure(ct_tls, &server.tls_ctx_config, 1) == C_ERR) { + serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info."); + exit(1); + } + } + + if (server.tls_port != 0) { + conn_index = connectionIndexByType(CONN_TYPE_TLS); + if (conn_index < 0) + serverPanic("Failed finding connection listener of %s", CONN_TYPE_TLS); + listener = &server.listeners[conn_index]; + listener->bindaddr = server.bindaddr; + listener->bindaddr_count = server.bindaddr_count; + listener->port = server.tls_port; + listener->ct = connectionByType(CONN_TYPE_TLS); + } + if (server.unixsocket != NULL) { + conn_index = connectionIndexByType(CONN_TYPE_UNIX); + if (conn_index < 0) + serverPanic("Failed finding connection listener of %s", CONN_TYPE_UNIX); + listener = &server.listeners[conn_index]; + listener->bindaddr = &server.unixsocket; + listener->bindaddr_count = 1; + listener->ct = connectionByType(CONN_TYPE_UNIX); + listener->priv = &server.unixsocketperm; /* Unix socket specified */ + } + + /* create all the configured listener, and add handler to start to accept */ + int listen_fds = 0; + for (int j = 0; j < CONN_TYPE_MAX; j++) { + listener = &server.listeners[j]; + if (listener->ct == NULL) + continue; + + if (connListen(listener) == C_ERR) { + serverLog(LL_WARNING, "Failed listening on port %u (%s), aborting.", listener->port, listener->ct->get_type(NULL)); + exit(1); + } + + if (createSocketAcceptHandler(listener, connAcceptHandler(listener->ct)) != C_OK) + serverPanic("Unrecoverable error creating %s listener accept handler.", listener->ct->get_type(NULL)); + + listen_fds += listener->count; + } + + if (listen_fds == 0) { + serverLog(LL_WARNING, "Configured to not listen anywhere, exiting."); + exit(1); + } +} + /* Some steps in server initialization need to be done last (after modules * are loaded). * Specifically, creation of threads due to a race bug in ld.so, in which @@ -7086,6 +7094,16 @@ int main(int argc, char **argv) { if (server.set_proc_title) redisSetProcTitle(NULL); redisAsciiArt(); checkTcpBacklogSettings(); + if (!server.sentinel_mode) { + moduleInitModulesSystemLast(); + moduleLoadFromQueue(); + } + ACLLoadUsersAtStartup(); + initListeners(); + if (server.cluster_enabled) { + clusterInit(); + } + InitServerLast(); if (!server.sentinel_mode) { /* Things not needed when running in Sentinel mode. */ @@ -7114,10 +7132,6 @@ int main(int argc, char **argv) { } #endif /* __arm64__ */ #endif /* __linux__ */ - moduleInitModulesSystemLast(); - moduleLoadFromQueue(); - ACLLoadUsersAtStartup(); - InitServerLast(); aofLoadManifestFromDisk(); loadDataFromDisk(); aofOpenIfNeededOnServerStart(); @@ -7148,8 +7162,6 @@ int main(int argc, char **argv) { redisCommunicateSystemd("READY=1\n"); } } else { - ACLLoadUsersAtStartup(); - InitServerLast(); sentinelIsRunning(); if (server.supervised_mode == SUPERVISED_SYSTEMD) { redisCommunicateSystemd("STATUS=Ready to accept connections\n"); diff --git a/src/server.h b/src/server.h index 65faa84af..8a0a2f5f6 100644 --- a/src/server.h +++ b/src/server.h @@ -81,6 +81,7 @@ typedef long long ustime_t; /* microsecond time type. */ #include "connection.h" /* Connection abstraction */ #define REDISMODULE_CORE 1 +typedef struct redisObject robj; #include "redismodule.h" /* Redis modules API defines. */ /* Following includes allow test functions to be called from Redis main() */ @@ -679,9 +680,6 @@ struct RedisModuleIO; struct RedisModuleDigest; struct RedisModuleCtx; struct moduleLoadQueueEntry; -struct redisObject; -struct RedisModuleDefragCtx; -struct RedisModuleInfoCtx; struct RedisModuleKeyOptCtx; struct RedisModuleCommand; @@ -701,20 +699,12 @@ typedef size_t (*moduleTypeFreeEffortFunc)(struct redisObject *key, const void * typedef void (*moduleTypeUnlinkFunc)(struct redisObject *key, void *value); typedef void *(*moduleTypeCopyFunc)(struct redisObject *fromkey, struct redisObject *tokey, const void *value); typedef int (*moduleTypeDefragFunc)(struct RedisModuleDefragCtx *ctx, struct redisObject *key, void **value); -typedef void (*RedisModuleInfoFunc)(struct RedisModuleInfoCtx *ctx, int for_crash_report); -typedef void (*RedisModuleDefragFunc)(struct RedisModuleDefragCtx *ctx); typedef size_t (*moduleTypeMemUsageFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size); typedef void (*moduleTypeFreeFunc2)(struct RedisModuleKeyOptCtx *ctx, void *value); typedef size_t (*moduleTypeFreeEffortFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value); typedef void (*moduleTypeUnlinkFunc2)(struct RedisModuleKeyOptCtx *ctx, void *value); typedef void *(*moduleTypeCopyFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value); -/* This callback type is called by moduleNotifyUserChanged() every time - * a user authenticated via the module API is associated with a different - * user or gets disconnected. This needs to be exposed since you can't cast - * a function pointer to (void *). */ -typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata); - /* The module type, which is referenced in each value of a given type, defines * the methods and links to the module exporting the type. */ @@ -786,7 +776,7 @@ typedef struct RedisModule RedisModule; /* This is a wrapper for the 'rio' streams used inside rdb.c in Redis, so that * the user does not have to take the total count of the written bytes nor * to care about error conditions. */ -typedef struct RedisModuleIO { +struct RedisModuleIO { size_t bytes; /* Bytes read / written so far. */ rio *rio; /* Rio stream. */ moduleType *type; /* Module type doing the operation. */ @@ -794,7 +784,7 @@ typedef struct RedisModuleIO { struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/ struct redisObject *key; /* Optional name of key processed */ int dbid; /* The dbid of the key being processed, -1 when unknown. */ -} RedisModuleIO; +}; /* Macro to initialize an IO context. Note that the 'ver' field is populated * inside rdb.c according to the version of the value to load. */ @@ -813,12 +803,12 @@ typedef struct RedisModuleIO { * a data structure, so that a digest can be created in a way that correctly * reflects the values. See the DEBUG DIGEST command implementation for more * background. */ -typedef struct RedisModuleDigest { +struct RedisModuleDigest { unsigned char o[20]; /* Ordered elements. */ unsigned char x[20]; /* Xored elements. */ struct redisObject *key; /* Optional name of key processed */ int dbid; /* The dbid of the key being processed */ -} RedisModuleDigest; +}; /* Just start with a digest composed of all zero bytes. */ #define moduleInitDigestContext(mdvar) do { \ @@ -849,7 +839,7 @@ typedef struct RedisModuleDigest { #define OBJ_SHARED_REFCOUNT INT_MAX /* Global object never destroyed. */ #define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */ #define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT -typedef struct redisObject { +struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or @@ -857,7 +847,7 @@ typedef struct redisObject { * and most significant 16 bits access time). */ int refcount; void *ptr; -} robj; +}; /* The a string name for an object's type as listed above * Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines, @@ -3269,6 +3259,7 @@ void *dictSdsDup(dict *d, const void *key); char *redisGitSHA1(void); char *redisGitDirty(void); uint64_t redisBuildId(void); +const char *redisBuildIdRaw(void); char *redisBuildIdString(void); /* Commands prototypes */ diff --git a/src/tls.c b/src/tls.c index be6bf4e1b..99cbea5a2 100644 --- a/src/tls.c +++ b/src/tls.c @@ -27,12 +27,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define REDISMODULE_CORE_MODULE /* A module that's part of the redis core, uses server.h too. */ #include "server.h" #include "connhelpers.h" #include "adlist.h" -#ifdef USE_OPENSSL +#if (USE_OPENSSL == 1 /* BUILD_YES */ ) || ((USE_OPENSSL == 2 /* BUILD_MODULE */) && (BUILD_TLS_MODULE == 2)) #include #include @@ -56,8 +57,8 @@ #define REDIS_TLS_PROTO_DEFAULT (REDIS_TLS_PROTO_TLSv1_2) #endif -static SSL_CTX *redis_tls_ctx = NULL; -static SSL_CTX *redis_tls_client_ctx = NULL; +SSL_CTX *redis_tls_ctx = NULL; +SSL_CTX *redis_tls_client_ctx = NULL; static int parseProtocolsConfig(const char *str) { int i, count = 0; @@ -1087,14 +1088,6 @@ static sds connTLSGetPeerCert(connection *conn_) { return cert_pem; } -static void *tlsGetCtx(void) { - return redis_tls_ctx; -} - -static void *tlsGetClientCtx(void) { - return redis_tls_client_ctx; -} - static ConnectionType CT_TLS = { /* connection type */ .get_type = connTLSGetType, @@ -1137,20 +1130,55 @@ static ConnectionType CT_TLS = { /* TLS specified methods */ .get_peer_cert = connTLSGetPeerCert, - .get_ctx = tlsGetCtx, - .get_client_ctx = tlsGetClientCtx }; -int RedisRegisterConnectionTypeTLS() -{ +int RedisRegisterConnectionTypeTLS() { return connTypeRegister(&CT_TLS); } #else /* USE_OPENSSL */ -int RedisRegisterConnectionTypeTLS() -{ +int RedisRegisterConnectionTypeTLS() { + serverLog(LL_VERBOSE, "Connection type %s not builtin", CONN_TYPE_TLS); return C_ERR; } #endif + +#if BUILD_TLS_MODULE == 2 /* BUILD_MODULE */ + +#include "release.h" + +int RedisModule_OnLoad(void *ctx, RedisModuleString **argv, int argc) { + UNUSED(argv); + UNUSED(argc); + + /* Connection modules must be part of the same build as redis. */ + if (strcmp(REDIS_BUILD_ID_RAW, redisBuildIdRaw())) { + serverLog(LL_NOTICE, "Connection type %s was not built together with the redis-server used.", CONN_TYPE_TLS); + return REDISMODULE_ERR; + } + + if (RedisModule_Init(ctx,"tls",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + /* Connection modules is available only bootup. */ + if ((RedisModule_GetContextFlags(ctx) & REDISMODULE_CTX_FLAGS_SERVER_STARTUP) == 0) { + serverLog(LL_NOTICE, "Connection type %s can be loaded only during bootup", CONN_TYPE_TLS); + return REDISMODULE_ERR; + } + + RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD); + + if(connTypeRegister(&CT_TLS) != C_OK) + return REDISMODULE_ERR; + + return REDISMODULE_OK; +} + +int RedisModule_OnUnload(void *arg) { + UNUSED(arg); + serverLog(LL_NOTICE, "Connection type %s can not be unloaded", CONN_TYPE_TLS); + return REDISMODULE_ERR; +} +#endif diff --git a/tests/instances.tcl b/tests/instances.tcl index 8faf6fb31..9cbb11a92 100644 --- a/tests/instances.tcl +++ b/tests/instances.tcl @@ -19,6 +19,7 @@ source ../support/test.tcl set ::verbose 0 set ::valgrind 0 set ::tls 0 +set ::tls_module 0 set ::pause_on_error 0 set ::dont_clean 0 set ::simulate_error 0 @@ -85,6 +86,10 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} { } if {$::tls} { + if {$::tls_module} { + puts $cfg [format "loadmodule %s/../../../src/redis-tls.so" [pwd]] + } + puts $cfg "tls-port $port" puts $cfg "tls-replication yes" puts $cfg "tls-cluster yes" @@ -271,13 +276,16 @@ proc parse_options {} { } elseif {$opt eq {--host}} { incr j set ::host ${val} - } elseif {$opt eq {--tls}} { + } elseif {$opt eq {--tls} || $opt eq {--tls-module}} { package require tls 1.6 ::tls::init \ -cafile "$::tlsdir/ca.crt" \ -certfile "$::tlsdir/client.crt" \ -keyfile "$::tlsdir/client.key" set ::tls 1 + if {$opt eq {--tls-module}} { + set ::tls_module 1 + } } elseif {$opt eq {--config}} { set val2 [lindex $::argv [expr $j+2]] dict set ::global_config $val $val2 @@ -293,6 +301,7 @@ proc parse_options {} { puts "--fail Simulate a test failure." puts "--valgrind Run with valgrind." puts "--tls Run tests in TLS mode." + puts "--tls-module Run tests in TLS mode with Redis module." puts "--host Use hostname instead of 127.0.0.1." puts "--config Extra config argument(s)." puts "--stop Blocks once the first test fails." diff --git a/tests/modules/defragtest.c b/tests/modules/defragtest.c index 221280df1..6a02a059f 100644 --- a/tests/modules/defragtest.c +++ b/tests/modules/defragtest.c @@ -35,7 +35,7 @@ static void createGlobalStrings(RedisModuleCtx *ctx, int count) } } -static int defragGlobalStrings(RedisModuleDefragCtx *ctx) +static void defragGlobalStrings(RedisModuleDefragCtx *ctx) { for (int i = 0; i < global_strings_len; i++) { RedisModuleString *new = RedisModule_DefragRedisModuleString(ctx, global_strings[i]); @@ -45,8 +45,6 @@ static int defragGlobalStrings(RedisModuleDefragCtx *ctx) global_defragged++; } } - - return 0; } static void FragInfo(RedisModuleInfoCtx *ctx, int for_crash_report) { diff --git a/tests/support/server.tcl b/tests/support/server.tcl index b673b70ae..6cc846b97 100644 --- a/tests/support/server.tcl +++ b/tests/support/server.tcl @@ -300,7 +300,7 @@ proc wait_server_started {config_file stdout pid} { set maxiter [expr {120*1000/$checkperiod}] ; # Wait up to 2 minutes. set port_busy 0 while 1 { - if {[regexp -- " PID: $pid" [exec cat $stdout]]} { + if {[regexp -- " PID: $pid.*Server initialized" [exec cat $stdout]]} { break } after $checkperiod @@ -464,6 +464,9 @@ proc start_server {options {code undefined}} { set data [split [exec cat "tests/assets/$baseconfig"] "\n"] set config {} if {$::tls} { + if {$::tls_module} { + lappend config_lines [list "loadmodule" [format "%s/src/redis-tls.so" [pwd]]] + } dict set config "tls-cert-file" [format "%s/tests/tls/server.crt" [pwd]] dict set config "tls-key-file" [format "%s/tests/tls/server.key" [pwd]] dict set config "tls-client-cert-file" [format "%s/tests/tls/client.crt" [pwd]] diff --git a/tests/support/util.tcl b/tests/support/util.tcl index 8153ad8bb..c7aef0f50 100644 --- a/tests/support/util.tcl +++ b/tests/support/util.tcl @@ -1039,3 +1039,25 @@ proc memory_usage {key} { } return $usage } + +# forward compatibility, lmap missing in TCL 8.5 +proc lmap args { + set body [lindex $args end] + set args [lrange $args 0 end-1] + set n 0 + set pairs [list] + foreach {varnames listval} $args { + set varlist [list] + foreach varname $varnames { + upvar 1 $varname var$n + lappend varlist var$n + incr n + } + lappend pairs $varlist $listval + } + set temp [list] + foreach {*}$pairs { + lappend temp [uplevel 1 $body] + } + set temp +} diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 5c951dda6..efa7a0a16 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -111,6 +111,7 @@ set ::traceleaks 0 set ::valgrind 0 set ::durable 0 set ::tls 0 +set ::tls_module 0 set ::stack_logging 0 set ::verbose 0 set ::quiet 0 @@ -611,6 +612,7 @@ proc print_help_screen {} { "--wait-server Wait after server is started (so that you can attach a debugger)." "--dump-logs Dump server log on test failure." "--tls Run tests in TLS mode." + "--tls-module Run tests in TLS mode with Redis module." "--host Run tests against an external host." "--port TCP port to use against external host." "--baseport Initial port number for spawned redis servers." @@ -659,13 +661,16 @@ for {set j 0} {$j < [llength $argv]} {incr j} { } } elseif {$opt eq {--quiet}} { set ::quiet 1 - } elseif {$opt eq {--tls}} { + } elseif {$opt eq {--tls} || $opt eq {--tls-module}} { package require tls 1.6 set ::tls 1 ::tls::init \ -cafile "$::tlsdir/ca.crt" \ -certfile "$::tlsdir/client.crt" \ -keyfile "$::tlsdir/client.key" + if {$opt eq {--tls-module}} { + set ::tls_module 1 + } } elseif {$opt eq {--host}} { set ::external 1 set ::host $arg diff --git a/tests/unit/moduleapi/infra.tcl b/tests/unit/moduleapi/infra.tcl index 7bfa7d4b3..1140e5ad5 100644 --- a/tests/unit/moduleapi/infra.tcl +++ b/tests/unit/moduleapi/infra.tcl @@ -5,18 +5,21 @@ test {modules config rewrite} { start_server {tags {"modules"}} { r module load $testmodule - assert_equal [lindex [lindex [r module list] 0] 1] infotest + set modules [lmap x [r module list] {dict get $x name}] + assert_not_equal [lsearch $modules infotest] -1 r config rewrite restart_server 0 true false - assert_equal [lindex [lindex [r module list] 0] 1] infotest + set modules [lmap x [r module list] {dict get $x name}] + assert_not_equal [lsearch $modules infotest] -1 assert_equal {OK} [r module unload infotest] r config rewrite restart_server 0 true false - assert_equal [llength [r module list]] 0 + set modules [lmap x [r module list] {dict get $x name}] + assert_equal [lsearch $modules infotest] -1 } } diff --git a/tests/unit/moduleapi/moduleconfigs.tcl b/tests/unit/moduleapi/moduleconfigs.tcl index 8ebce3514..2b28fc307 100644 --- a/tests/unit/moduleapi/moduleconfigs.tcl +++ b/tests/unit/moduleapi/moduleconfigs.tcl @@ -5,7 +5,7 @@ start_server {tags {"modules"}} { r module load $testmodule test {Config get commands work} { # Make sure config get module config works - assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs + assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes" assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no" assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024" @@ -94,7 +94,7 @@ start_server {tags {"modules"}} { test {test loadex functionality} { r module loadex $testmodule CONFIG moduleconfigs.mutable_bool no CONFIG moduleconfigs.immutable_bool yes CONFIG moduleconfigs.memory_numeric 2mb CONFIG moduleconfigs.string tclortickle - assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs + assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no" assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool yes" assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 2097152" @@ -157,7 +157,7 @@ start_server {tags {"modules"}} { test {test config rewrite with dynamic load} { #translates to: super \0secret password r module loadex $testmodule CONFIG moduleconfigs.string \x73\x75\x70\x65\x72\x20\x00\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64 ARGS - assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs + assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}" r config set moduleconfigs.mutable_bool yes r config set moduleconfigs.memory_numeric 750 @@ -207,7 +207,7 @@ start_server {tags {"modules"}} { test {test 1.module load 2.config rewrite 3.module unload 4.config rewrite works} { # Configs need to be removed from the old config file in this case. r module loadex $testmodule CONFIG moduleconfigs.memory_numeric 500 ARGS - assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs + assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1 r config rewrite r module unload moduleconfigs r config rewrite @@ -217,34 +217,18 @@ start_server {tags {"modules"}} { } test {startup moduleconfigs} { # No loadmodule directive - set nomodload [start_server [list overrides [list moduleconfigs.string "hello"]]] - wait_for_condition 100 50 { - ! [is_alive $nomodload] - } else { - fail "startup should've failed with no load and module configs supplied" - } - set stdout [dict get $nomodload stdout] - assert_equal [count_message_lines $stdout "Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting"] 1 + catch {exec src/redis-server --moduleconfigs.string "hello"} err + assert_match {*Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting*} $err # Bad config value - set badconfig [start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "rejectisfreed"]]] - wait_for_condition 100 50 { - ! [is_alive $badconfig] - } else { - fail "startup with bad moduleconfigs should've failed" - } - set stdout [dict get $badconfig stdout] - assert_equal [count_message_lines $stdout "Issue during loading of configuration moduleconfigs.string : Cannot set string to 'rejectisfreed'"] 1 + catch {exec src/redis-server --loadmodule "$testmodule" --moduleconfigs.string "rejectisfreed"} err + assert_match {*Issue during loading of configuration moduleconfigs.string : Cannot set string to 'rejectisfreed'*} $err - set noload [start_server [list overrides [list loadmodule "$testmodule noload" moduleconfigs.string "hello"]]] - wait_for_condition 100 50 { - ! [is_alive $noload] - } else { - fail "startup with moduleconfigs and no loadconfigs call should've failed" - } - set stdout [dict get $noload stdout] - assert_equal [count_message_lines $stdout "Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module."] 1 + # missing LoadConfigs call + catch {exec src/redis-server --loadmodule "$testmodule" noload --moduleconfigs.string "hello"} err + assert_match {*Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module.*} $err + # successful start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two four"]] { assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup" assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"