]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cli: add a new experimental "set var" command
authorWilly Tarreau <w@1wt.eu>
Fri, 26 Mar 2021 14:19:50 +0000 (15:19 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 26 Mar 2021 15:57:43 +0000 (16:57 +0100)
set var <name> <expression>
  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 preceeded 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").

Just like for "set-var" in the global section, the command uses a temporary
dummy proxy to create a temporary "set-var(name)" rule to assign the value.

The reg test was updated to verify that an updated global variable is properly
reflected in subsequent HTTP responses.

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

index bd3622aa0600069f3495dae56bb15feccd2f994c..c7eb7fff4ecf1349f703e351450f3ac23529609e 100644 (file)
@@ -1776,6 +1776,17 @@ get var <name>
   'proc.' otherwise no variable will be found. This command requires levels
   "operator" or "admin".
 
+set var <name> <expression>
+  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 preceeded 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").
+
 get weight <backend>/<server>
   Report the current weight and the initial weight of server <server> in
   backend <backend> or an error if either doesn't exist. The initial weight is
index b57156ce9d3532bf5d26679b1b59b2ee569560d4..01e25b96d86f6d2c5bd4b2e6b4baf9c8f7c02485 100644 (file)
@@ -64,3 +64,15 @@ haproxy h1 -cli {
     send "get var proc.int5"
     expect ~ "^proc.int5: type=sint value=<20>$"
 }
+
+haproxy h1 -cli {
+    send "experimental-mode on; set var proc.str str(updated)"
+    expect ~ .*
+}
+
+client c3 -connect ${h1_fe1_sock} {
+    txreq -req GET -url /req3_1
+    rxresp
+    expect resp.status == 200
+    expect resp.http.x-var ~ "proc=40 sess=40 req=20 str=updated uuid=[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*-[0-9a-f]*"
+} -run
index 11efd55a4a9bd3e4879e359f7363fb34d40d5dc4..cf92005f59ecd7a06b8ed8edfa79c1d32dab9c47 100644 (file)
@@ -912,6 +912,68 @@ 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>" */
+static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
+{
+       struct proxy px = {
+               .id = "CLI",
+               .conf.args.file = "CLI",
+               .conf.args.line = 0,
+       };
+       struct act_rule rule = {
+               .arg.vars.scope = SCOPE_PROC,
+               .from = ACT_F_CLI_PARSER,
+       };
+       enum act_parse_ret p_ret;
+       char *old_arg2;
+       char *tmp_arg2;
+       char *err = NULL;
+       int arg = 2; // variable name
+       int nberr;
+
+       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");
+
+       tmp_arg2 = NULL;
+       if (!memprintf(&tmp_arg2, "set-var(%s)", 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;
+
+       if (p_ret != ACT_RET_PRS_OK)
+               goto fail;
+
+       if (rule.arg.vars.scope != SCOPE_PROC) {
+               memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1], args[2]);
+               goto fail;
+       }
+
+       err = NULL;
+       nberr = smp_resolve_args(&px, &err);
+       if (nberr) {
+               release_sample_expr(rule.arg.vars.expr);
+               indent_msg(&err, 2);
+               goto fail;
+       }
+
+       action_store(&rule, &px, NULL, NULL, 0);
+       release_sample_expr(rule.arg.vars.expr);
+       appctx->st0 = CLI_ST_PROMPT;
+       return 0;
+ fail:
+       return cli_dynerr(appctx, err);
+}
+
 static int vars_max_size(char **args, int section_type, struct proxy *curpx,
                          const struct proxy *defpx, const char *file, int line,
                          char **err, unsigned int *limit)
@@ -1066,6 +1128,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 },
        { { NULL }, NULL, NULL, NULL }
 }};
 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);