diff options
-rw-r--r-- | .github/workflows/ci.yml | 3 | ||||
-rw-r--r-- | .github/workflows/daily.yml | 12 | ||||
-rw-r--r-- | TLS.md | 17 | ||||
-rw-r--r-- | deps/Makefile | 2 | ||||
-rw-r--r-- | src/Makefile | 39 | ||||
-rw-r--r-- | src/connection.h | 13 | ||||
-rwxr-xr-x | src/mkreleasehdr.sh | 2 | ||||
-rw-r--r-- | src/module.c | 8 | ||||
-rw-r--r-- | src/redismodule.h | 44 | ||||
-rw-r--r-- | src/release.c | 7 | ||||
-rw-r--r-- | src/sentinel.c | 16 | ||||
-rw-r--r-- | src/server.c | 152 | ||||
-rw-r--r-- | src/server.h | 25 | ||||
-rw-r--r-- | src/tls.c | 62 | ||||
-rw-r--r-- | tests/instances.tcl | 11 | ||||
-rw-r--r-- | tests/modules/defragtest.c | 4 | ||||
-rw-r--r-- | tests/support/server.tcl | 5 | ||||
-rw-r--r-- | tests/support/util.tcl | 22 | ||||
-rw-r--r-- | tests/test_helper.tcl | 7 | ||||
-rw-r--r-- | tests/unit/moduleapi/infra.tcl | 9 | ||||
-rw-r--r-- | 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 @@ -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 <stdio.h> #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 */ @@ -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 <openssl/conf.h> #include <openssl/ssl.h> @@ -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 <host> Use hostname instead of 127.0.0.1." puts "--config <k> <v> 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 <addr> Run tests against an external host." "--port <port> TCP port to use against external host." "--baseport <port> 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" |