diff --git a/src/config.c b/src/config.c index 3e0984208..237dc82ad 100644 --- a/src/config.c +++ b/src/config.c @@ -1064,6 +1064,8 @@ struct rewriteConfigState { sds *lines; /* Current lines as an array of sds strings */ int has_tail; /* True if we already added directives that were not present in the original config file. */ + int force_all; /* True if we want all keywords to be force + written. Currently only used for testing. */ }; /* Append the new line to the current configuration state. */ @@ -1110,6 +1112,7 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) { state->numlines = 0; state->lines = NULL; state->has_tail = 0; + state->force_all = 0; if (fp == NULL) return state; /* Read the old file line by line, populate the state. */ @@ -1188,7 +1191,7 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *opti rewriteConfigMarkAsProcessed(state,option); - if (!l && !force) { + if (!l && !force && !state->force_all) { /* Option not used previously, and we are not forced to use it. */ sdsfree(line); sdsfree(o); @@ -1612,15 +1615,18 @@ cleanup: * * Configuration parameters that are at their default value, unless already * explicitly included in the old configuration file, are not rewritten. + * The force_all flag overrides this behavior and forces everything to be + * written. This is currently only used for testing purposes. * * On error -1 is returned and errno is set accordingly, otherwise 0. */ -int rewriteConfig(char *path) { +int rewriteConfig(char *path, int force_all) { struct rewriteConfigState *state; sds newcontent; int retval; /* Step 1: read the old config into our rewrite state. */ if ((state = rewriteConfigReadOldFile(path)) == NULL) return -1; + if (force_all) state->force_all = 1; /* Step 2: rewrite every single option, replacing or appending it inside * the rewrite state. */ @@ -2427,7 +2433,7 @@ NULL addReplyError(c,"The server is running without a config file"); return; } - if (rewriteConfig(server.configfile) == -1) { + if (rewriteConfig(server.configfile, 0) == -1) { serverLog(LL_WARNING,"CONFIG REWRITE failed: %s", strerror(errno)); addReplyErrorFormat(c,"Rewriting config file: %s", strerror(errno)); } else { diff --git a/src/debug.c b/src/debug.c index 5dd9ec849..82b34e08d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -408,6 +408,7 @@ void debugCommand(client *c) { "STRUCTSIZE -- Return the size of different Redis core C structures.", "ZIPLIST -- Show low level info about the ziplist encoding.", "STRINGMATCH-TEST -- Run a fuzz tester against the stringmatchlen() function.", +"CONFIG-REWRITE-FORCE-ALL -- Like CONFIG REWRITE but writes all configuration options, including keywords not listed in original configuration file or default values.", #ifdef USE_JEMALLOC "MALLCTL [] -- Get or set a malloc tunning integer.", "MALLCTL-STR [] -- Get or set a malloc tunning string.", @@ -805,6 +806,12 @@ NULL { stringmatchlen_fuzz_test(); addReplyStatus(c,"Apparently Redis did not crash: test passed"); + } else if (!strcasecmp(c->argv[1]->ptr,"config-rewrite-force-all") && c->argc == 2) + { + if (rewriteConfig(server.configfile, 1) == -1) + addReplyError(c, "CONFIG-REWRITE-FORCE-ALL failed"); + else + addReply(c, shared.ok); #ifdef USE_JEMALLOC } else if(!strcasecmp(c->argv[1]->ptr,"mallctl") && c->argc >= 3) { mallctl_int(c, c->argv+2, c->argc-2); diff --git a/src/sentinel.c b/src/sentinel.c index d93b26131..3cf496731 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -1954,7 +1954,7 @@ void sentinelFlushConfig(void) { int rewrite_status; server.hz = CONFIG_DEFAULT_HZ; - rewrite_status = rewriteConfig(server.configfile); + rewrite_status = rewriteConfig(server.configfile, 0); server.hz = saved_hz; if (rewrite_status == -1) goto werr; diff --git a/src/server.c b/src/server.c index 8a443fd79..b1d7614f5 100644 --- a/src/server.c +++ b/src/server.c @@ -2552,7 +2552,7 @@ int restartServer(int flags, mstime_t delay) { /* Config rewriting. */ if (flags & RESTART_SERVER_CONFIG_REWRITE && server.configfile && - rewriteConfig(server.configfile) == -1) + rewriteConfig(server.configfile, 0) == -1) { serverLog(LL_WARNING,"Can't restart: configuration rewrite process " "failed"); diff --git a/src/server.h b/src/server.h index 05b7d1dd7..45f716390 100644 --- a/src/server.h +++ b/src/server.h @@ -2101,7 +2101,7 @@ void appendServerSaveParams(time_t seconds, int changes); void resetServerSaveParams(void); struct rewriteConfigState; /* Forward declaration to export API. */ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force); -int rewriteConfig(char *path); +int rewriteConfig(char *path, int force_all); void initConfigValues(); /* db.c -- Keyspace access API */ diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl index 869f34732..c8bc976cf 100644 --- a/tests/unit/introspection.tcl +++ b/tests/unit/introspection.tcl @@ -147,4 +147,28 @@ start_server {tags {"introspection"}} { } } + + # Do a force-all config rewrite and make sure we're able to parse + # it. + test {CONFIG REWRITE sanity} { + # Capture state of config before + set configs {} + foreach {k v} [r config get *] { + dict set configs $k $v + } + + # Rewrite entire configuration, restart and confirm the + # server is able to parse it and start. + assert_equal [r debug config-rewrite-force-all] "OK" + restart_server 0 0 + assert_equal [r ping] "PONG" + + # Verify no changes were introduced + dict for {k v} $configs { + assert_equal $v [lindex [r config get $k] 1] + } + } + + # Config file at this point is at a wierd state, and includes all + # known keywords. Might be a good idea to avoid adding tests here. }