]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: vars: also support format strings in CLI's "set var" command
authorWilly Tarreau <w@1wt.eu>
Fri, 3 Sep 2021 07:47:37 +0000 (09:47 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 3 Sep 2021 09:01:48 +0000 (11:01 +0200)
Most often "set var" on the CLI is used to set a string, and using only
expressions is not always convenient, particularly when trying to
concatenate variables sur as host names and paths.

Now the "set var" command supports an optional keyword before the value
to indicate its type. "expr" takes an expression just like before this
patch, and "fmt" a format string, making it work like the "set-var-fmt"
actions.

The VTC was updated to include a test on the format string.

doc/management.txt
reg-tests/sample_fetches/vars.vtc
src/vars.c

index 58d06e48c9c37961e9fe88a149e7794421ecac39..c7a8e4884ca12e5b4eb20464ad0a9de4b2a9437d 100644 (file)
@@ -2254,15 +2254,18 @@ set timeout cli <delay>
   some indicators without being disconnected. The delay is passed in seconds.
 
 set var <name> <expression>
+set var <name> expr <expression>
+set var <name> fmt <format>
   Allows to set or overwrite the process-wide variable 'name' with the result
-  of expression <expression>. Only process-wide variables may be used, so the
-  name must begin with 'proc.' otherwise no variable will be set. The
-  <expression> may only involve "internal" sample fetch keywords and converters
-  even though the most likely useful ones will be str('something') or int().
-  Note that the command line parser doesn't know about quotes, so any space in
-  the expression must be preceded by a backslash. This command requires levels
-  "operator" or "admin". This command is only supported on a CLI connection
-  running in experimental mode (see "experimental-mode on").
+  of expression <expression> or format string <format>. Only process-wide
+  variables may be used, so the name must begin with 'proc.' otherwise no
+  variable will be set. The <expression> and <format> may only involve
+  "internal" sample fetch keywords and converters even though the most likely
+  useful ones will be str('something'), int(), simple strings or references to
+  other variables. Note that the command line parser doesn't know about quotes,
+  so any space in the expression must be preceded by a backslash. This command
+  requires levels "operator" or "admin". This command is only supported on a
+  CLI connection running in experimental mode (see "experimental-mode on").
 
 set weight <backend>/<server> <weight>[%]
   Change a server's weight to the value passed in argument. If the value ends
index 82d7718bef25169d64a44eabbc22f9e8d3637ba8..5dc7b44000acbdd51d63ea65afaa00062fdf77bd 100644 (file)
@@ -71,7 +71,7 @@ haproxy h1 -cli {
 }
 
 haproxy h1 -cli {
-    send "experimental-mode on; set var proc.str str(updated)"
+    send "experimental-mode on; set var proc.str str(updating); set var proc.str fmt %[var(proc.str),regsub(ing,ed)]"
     expect ~ .*
 }
 
index 168ead671c797f7df1e0a6b2abea7b2f83962307..6208093d27b305b4129e9cce8b3afe95cb165c0b 100644 (file)
@@ -1015,7 +1015,11 @@ static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *app
        return cli_msg(appctx, LOG_INFO, trash.area);
 }
 
-/* parse CLI's "set var <name> <expression>" */
+/* parse CLI's "set var <name>". It accepts:
+ *  - set var <name> <expression>
+ *  - set var <name> expr <expression>
+ *  - set var <name> fmt <format>
+ */
 static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
 {
        struct proxy px = {
@@ -1027,31 +1031,50 @@ static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *app
                .arg.vars.scope = SCOPE_PROC,
                .from = ACT_F_CLI_PARSER,
        };
+       enum obj_type objt = OBJ_TYPE_NONE;
+       struct session *sess = NULL;
        enum act_parse_ret p_ret;
-       char *old_arg2;
-       char *tmp_arg2;
+       const char *tmp_args[3];
+       int tmp_arg;
+       char *tmp_act;
        char *err = NULL;
-       int arg = 2; // variable name
        int nberr;
+       int use_fmt = 0;
 
        LIST_INIT(&px.conf.args.list);
 
        if (!cli_has_level(appctx, ACCESS_LVL_OPER))
                return 1;
 
-       if (!*args[2] || !*args[3])
-               return cli_err(appctx, "Missing process-wide variable identifier and expression.\n");
+       if (!*args[2])
+               return cli_err(appctx, "Missing process-wide variable identifier.\n");
+
+       if (!*args[3])
+               return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n");
+
+       if (*args[4]) {
+               /* this is the long format */
+               if (strcmp(args[3], "fmt") == 0)
+                       use_fmt = 1;
+               else if (strcmp(args[3], "expr") != 0) {
+                       memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]);
+                       goto fail;
+               }
+       }
 
-       tmp_arg2 = NULL;
-       if (!memprintf(&tmp_arg2, "set-var(%s)", args[2])) {
+       tmp_act = NULL;
+       if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) {
                memprintf(&err, "memory allocation error.");
                goto fail;
        }
 
        /* parse_store() will always return a message in <err> on error */
-       old_arg2 = args[2]; args[2] = tmp_arg2;
-       p_ret = parse_store((const char **)(args + 1), &arg, &px, &rule, &err);
-       free(args[2]); args[2] = old_arg2;
+       tmp_args[0] = tmp_act;
+       tmp_args[1] = (*args[4]) ? args[4] : args[3];
+       tmp_args[2] = "";
+       tmp_arg = 1; // must point to the first arg after the action
+       p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err);
+       free(tmp_act);
 
        if (p_ret != ACT_RET_PRS_OK)
                goto fail;
@@ -1069,8 +1092,17 @@ static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *app
                goto fail;
        }
 
-       action_store(&rule, &px, NULL, NULL, 0);
+       if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
+               release_sample_expr(rule.arg.vars.expr);
+               memprintf(&err, "memory allocation error.");
+               goto fail;
+       }
+
+       action_store(&rule, &px, sess, NULL, 0);
        release_sample_expr(rule.arg.vars.expr);
+       if (sess)
+               session_free(sess);
+
        appctx->st0 = CLI_ST_PROMPT;
        return 0;
  fail:
@@ -1239,7 +1271,7 @@ INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
        { { "get",   "var", NULL }, "get var <name>                          : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
-       { { "set",   "var", NULL }, "set var <name> <expr>                   : set variable from an expression",              vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
+       { { "set",   "var", NULL }, "set var <name> [fmt|expr] {<fmt>|<expr>}: set variable from an expression or a format",  vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
        { { NULL }, NULL, NULL, NULL }
 }};
 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);