]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stream: support use-server rules with dynamic names
authorJerome Magnin <jmagnin@haproxy.com>
Sun, 29 Mar 2020 07:37:12 +0000 (09:37 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 29 Mar 2020 07:55:10 +0000 (09:55 +0200)
With server-template was introduced the possibility to scale the
number of servers in a backend without needing a configuration change
and associated reload. On the other hand it became impractical to
write use-server rules for these servers as they would only accept
existing server labels as argument. This patch allows the use of
log-format notation to describe targets of a use-server rules, such
as in the example below:

  listen test
    bind *:1234
    use-server %[hdr(srv)] if { hdr(srv) -m found }
    use-server s1 if { path / }
    server s1 127.0.0.1:18080
    server s2 127.0.0.1:18081

If a use-server rule is applied because it was conditionned by an
ACL returning true, but the target of the use-server rule cannot be
resolved, no other use-server rule is evaluated and we fall back to
load balancing.

This feature was requested on the ML, and bumped with issue #563.

doc/configuration.txt
include/types/arg.h
include/types/proxy.h
src/cfgparse-listen.c
src/cfgparse.c
src/stream.c

index 8eb3f8e84ae3c35c1821567b258407d6504497e2..8347e8a4df887255df82af5fa3dab2ec07016fd5 100644 (file)
@@ -11141,7 +11141,8 @@ use-server <server> unless <condition>
   May be used in sections :   defaults | frontend | listen | backend
                                   no   |    no    |   yes  |   yes
   Arguments :
-    <server>    is the name of a valid server in the same backend section.
+    <server>    is the name of a valid server in the same backend section
+                or a "log-format" string resolving to a server name.
 
     <condition> is a condition composed of ACLs, as described in section 7.
 
@@ -11186,6 +11187,13 @@ use-server <server> unless <condition>
      # all the rest is forwarded to this server
      server  default 192.168.0.2:443 check
 
+  When <server> is a simple name, it is checked against existing servers in the
+  configuration and an error is reported if the specified server does not exist.
+  If it is a log-format, no check is performed when parsing the configuration,
+  and if we can't resolve a valid server name at runtime but the use-server rule
+  was conditionned by an ACL returning true, no other use-server rule is applied
+  and we fall back to load balancing.
+
   See also: "use_backend", section 5 about server and section 7 about ACLs.
 
 
index a9778f2eceac044809f26c4c0d36112e82f924e3..80e0b0a7be73c69ad703b8c68a7d2564be4b7828 100644 (file)
@@ -81,6 +81,7 @@ enum {
        ARGC_SRV,      /* server line */
        ARGC_SPOE,     /* spoe message args */
        ARGC_UBK,      /* use_backend message */
+       ARGC_USRV,     /* use-server message */
 };
 
 /* flags used when compiling and executing regex */
index a3da428e80ae3b86257f5094a112b9e94028638d..84ac8d058dbe16ed7ccaf913f7c0aff7a18e6c00 100644 (file)
@@ -509,10 +509,14 @@ struct switching_rule {
 struct server_rule {
        struct list list;                       /* list linked to from the proxy */
        struct acl_cond *cond;                  /* acl condition to meet */
+       int dynamic;
        union {
                struct server *ptr;             /* target server */
                char *name;                     /* target server name during config parsing */
        } srv;
+       struct list expr;               /* logformat expression to use for dynamic rules */
+       char *file;
+       int line;
 };
 
 struct persist_rule {
index 9bffee5d2aa1b04b801bf60a46a3611c09fc903d..ffc575c39a4a0c6ef0f68573cc43894de8986844 100644 (file)
@@ -1592,6 +1592,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                rule = calloc(1, sizeof(*rule));
                rule->cond = cond;
                rule->srv.name = strdup(args[1]);
+               rule->line = linenum;
+               rule->file = strdup(file);
                LIST_INIT(&rule->list);
                LIST_ADDQ(&curproxy->server_rules, &rule->list);
                curproxy->be_req_ana |= AN_REQ_SRV_RULES;
index 2c80083120f4129fb3fc24790054dd419a3ef627..9b4a0be37936738797b7f36b47f682ea268e97af 100644 (file)
@@ -2701,7 +2701,40 @@ int check_config_validity()
 
                /* find the target server for 'use_server' rules */
                list_for_each_entry(srule, &curproxy->server_rules, list) {
-                       struct server *target = findserver(curproxy, srule->srv.name);
+                       struct server *target;
+                       struct logformat_node *node;
+                       char *server_name;
+
+                       /* We try to parse the string as a log format expression. If the result of the parsing
+                        * is only one entry containing a single string, then it's a standard string corresponding
+                        * to a static rule, thus the parsing is cancelled and we fall back to setting srv.ptr.
+                        */
+                       server_name = srule->srv.name;
+                       LIST_INIT(&srule->expr);
+                       curproxy->conf.args.ctx = ARGC_USRV;
+                       err = NULL;
+                       if (!parse_logformat_string(server_name, curproxy, &srule->expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) {
+                               ha_alert("Parsing [%s:%d]; use-server rule failed to parse log-format '%s' : %s.\n",
+                                               srule->file, srule->line, server_name, err);
+                               free(err);
+                               cfgerr++;
+                               continue;
+                       }
+                       node = LIST_NEXT(&srule->expr, struct logformat_node *, list);
+
+                       if (!LIST_ISEMPTY(&srule->expr)) {
+                               if (node->type != LOG_FMT_TEXT || node->list.n != &srule->expr) {
+                                       srule->dynamic = 1;
+                                       free(server_name);
+                                       continue;
+                               }
+                               free(node->arg);
+                               free(node);
+                       }
+
+                       srule->dynamic = 0;
+                       srule->srv.name = server_name;
+                       target = findserver(curproxy, srule->srv.name);
 
                        if (!target) {
                                ha_alert("config : %s '%s' : unable to find server '%s' referenced in a 'use-server' rule.\n",
index c059a7a2252baab7a497ae3e6f33d65e6a541abc..8b9d885e745c49625321be98f1d62f6c5653b098 100644 (file)
@@ -1169,7 +1169,20 @@ static int process_server_rules(struct stream *s, struct channel *req, int an_bi
                                ret = !ret;
 
                        if (ret) {
-                               struct server *srv = rule->srv.ptr;
+                               struct server *srv;
+
+                               if (rule->dynamic) {
+                                       struct buffer *tmp = get_trash_chunk();
+
+                                       if (!build_logline(s, tmp->area, tmp->size, &rule->expr))
+                                               break;
+
+                                       srv = findserver(s->be, tmp->area);
+                                       if (!srv)
+                                               break;
+                               }
+                               else
+                                       srv = rule->srv.ptr;
 
                                if ((srv->cur_state != SRV_ST_STOPPED) ||
                                    (px->options & PR_O_PERSIST) ||