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 <pizhenwei@bytedance.com>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
This commit is contained in:
Oran Agra 2022-08-22 15:53:56 +08:00
parent 89e1148688
commit 4faddf18ca
21 changed files with 302 additions and 198 deletions

View File

@ -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

View File

@ -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

17
TLS.md
View File

@ -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 \

2
deps/Makefile vendored
View File

@ -44,7 +44,7 @@ distclean:
.PHONY: distclean
ifeq ($(BUILD_TLS),yes)
ifneq (,$(filter $(BUILD_TLS),yes module))
HIREDIS_MAKE_FLAGS = USE_SSL=1
endif

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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 */

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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 */

View File

@ -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

View File

@ -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."

View File

@ -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) {

View File

@ -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]]

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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"