diff --git a/doc/configuration.txt b/doc/configuration.txt index af815305c..d7ad7888b 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1670,6 +1670,7 @@ The following keywords are supported in the "global" section : - tune.lua.maxmem - tune.lua.service-timeout - tune.lua.session-timeout + - tune.lua.smp-preserve-bool - tune.lua.task-timeout - tune.max-checks-per-thread - tune.maxaccept @@ -3989,6 +3990,25 @@ tune.lua.task-timeout remain alive during of the lifetime of HAProxy. For example, a task used to check servers. +tune.lua.smp-preserve-bool { on | off } + Explicitly tell haproxy how haproxy sample objects should be handled when + pushed to Lua. Indeed, when using native converters or sample fetches from + Lua script, haproxy converts the smp type to equivalent Lua type. Because + of historical implementation, there is an ambiguity around boolean + handling: when doing Lua -> haproxy smp conversion, booleans are properly + preserved, but when doing haproxy smp -> Lua conversion, booleans were + converted to integers. This means that a sample fetch or converter returning + a boolean would return an integer 0 or 1 when leveraged from Lua. + Unfortunately, in Lua, booleans and integers are not interchangeable. Thus, + to avoid ambiguities, "tune.lua.smp-preserve-bool" must explicitly be set to + either "on" (which means dropping the historical behavior for better + consistency) or "off" (enforce historical behavior to prevent existing script + logic from misbehaving). If the option is not set explicitly and a Lua script + is loaded from the configuration, haproxy will emit a warning, and the option + will implicitly default to "off" to match with the historical behavior. When + possible, it is recommended to set this option to "on" after ensuring that + in-use Lua scripts are properly handling bool haproxy samples as booleans. + tune.max-checks-per-thread Sets the number of active checks per thread above which a thread will actively try to search a less loaded thread to run the health check, or diff --git a/src/hlua.c b/src/hlua.c index e1a8b431a..4889b6776 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -84,6 +84,18 @@ enum hlua_log_opt { /* default log options, made of flags in hlua_log_opt */ static uint hlua_log_opts = HLUA_LOG_LOGGERS_ON | HLUA_LOG_STDERR_AUTO; +#define HLUA_SMP_PRESERVE_BOOL_UNK 0x0 +#define HLUA_SMP_PRESERVE_BOOL_ON 0x1 +#define HLUA_SMP_PRESERVE_BOOL_OFF 0X2 + +/* used to know how to deal with smp2lua bool handling, option implicitly + * defaults to historical behavior (off), but it has to be set explicitly + * to avoid ambiguity, else a warning will be emitted + * + * FIXME: make it default to ON in 3.3?? + */ +static uint8_t hlua_smp_preserve_bool = HLUA_SMP_PRESERVE_BOOL_UNK; + /* Lua uses longjmp to perform yield or throwing errors. This * macro is used only for identifying the function that can * not return because a longjmp is executed. @@ -1085,9 +1097,14 @@ __LJMP static int hlua_smp2lua(lua_State *L, struct sample *smp) { switch (smp->data.type) { case SMP_T_SINT: - case SMP_T_BOOL: lua_pushinteger(L, smp->data.u.sint); break; + case SMP_T_BOOL: + if (hlua_smp_preserve_bool == HLUA_SMP_PRESERVE_BOOL_ON) + lua_pushboolean(L, !!smp->data.u.sint); + else + lua_pushinteger(L, smp->data.u.sint); + break; case SMP_T_BIN: case SMP_T_STR: @@ -12936,6 +12953,24 @@ static int hlua_cfg_parse_log_stderr(char **args, int section_type, struct proxy return 0; } +static int hlua_cfg_parse_smp_preserve_bool(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (too_many_args(1, args, err, NULL)) + return -1; + + if (strcmp(args[1], "on") == 0) + hlua_smp_preserve_bool = HLUA_SMP_PRESERVE_BOOL_ON; + else if (strcmp(args[1], "off") == 0) + hlua_smp_preserve_bool = HLUA_SMP_PRESERVE_BOOL_OFF; + else { + memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]); + return -1; + } + return 0; +} + /* This function is called by the main configuration key "lua-load". It loads and * execute an lua file during the parsing of the HAProxy configuration file. It is * the main lua entry point. @@ -12955,6 +12990,21 @@ static int hlua_load_state(char **args, lua_State *L, char **err) int error; int nargs; + if (ONLY_ONCE()) { + /* we know we get there if "lua-load" or "lua-load-per-thread" was + * used in the config + */ + if (hlua_smp_preserve_bool == HLUA_SMP_PRESERVE_BOOL_UNK) { + /* hlua_smp_preserve_bool tunable must be explicitly set to + * avoid ambiguity, so we raise a warning (but only if lua + * is actually used + */ + ha_warning("hlua: please set \"tune.lua.smp-preserve-bool\" tunable to " + "either \"on\" or \"off\" explicitly to avoid ambiguities. " + "Defaulting to \"off\".\n"); + } + } + /* Just load and compile the file. */ error = luaL_loadfile(L, args[0]); if (error) { @@ -13186,6 +13236,7 @@ static struct cfg_kw_list cfg_kws = {{ },{ { CFG_GLOBAL, "tune.lua.maxmem", hlua_parse_maxmem }, { CFG_GLOBAL, "tune.lua.log.loggers", hlua_cfg_parse_log_loggers }, { CFG_GLOBAL, "tune.lua.log.stderr", hlua_cfg_parse_log_stderr }, + { CFG_GLOBAL, "tune.lua.smp-preserve-bool", hlua_cfg_parse_smp_preserve_bool }, { 0, NULL, NULL }, }};