]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: server: define _srv_parse_from() for server "from" keyword
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 8 Jul 2026 08:05:21 +0000 (10:05 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 3 Aug 2026 14:52:09 +0000 (16:52 +0200)
Prepare the support for a new server keyword "from".

This keyword has special constraints : it is a positional one as it can
only be specified once, after the server address and before the other
parameters.

The purpose of this keyword will be to define server settings
inheritance outside of the default-server of the current backend. It
will also be useful for dynamic servers which currently do not inherit
from a default-server.

doc/configuration.txt
reg-tests/server/from_keyword.vtc [new file with mode: 0644]
src/server.c

index bcdd50c254e55305f054eaa31d385b1554319488..b8a5704665cfa48f12eee518442e41741863db8c 100644 (file)
@@ -7352,7 +7352,7 @@ declare capture [ request | response ] len <length>
             "http-request capture" and "http-response capture".
 
 
-default-server [param*]
+default-server [from <origin>] [param*]
   Change default options for a server in a backend
 
   May be used in the following contexts: tcp, http
@@ -7361,6 +7361,11 @@ default-server [param*]
                                  yes   |    no    |   yes  |   yes
 
   Arguments:
+    <origin>  instructs to reinitialize first the set of options to their
+              default values and then reusing the settings from the designated
+              server or default-server instance. See section 5.2 about server
+              options for a complete description of the supported arguments.
+
     <param*>  is a list of parameters for this server. The "default-server"
               keyword accepts an important number of options and has a complete
               section dedicated to it. Please refer to section 5 for more
@@ -12525,7 +12530,7 @@ server-state-file-name [ { use-backend-name | <file> } ]
   See also: "server-state-base", "load-server-state-from-file", and
   "show servers state"
 
-server-template <prefix> <num | range> <fqdn>[:<port>] [params*]
+server-template <prefix> <num | range> <fqdn>[:<port>] [from <origin>] [params*]
   Set a template to initialize servers with shared parameters.
   The names of these servers are built from <prefix> and <num | range> parameters.
 
@@ -12547,6 +12552,10 @@ server-template <prefix> <num | range> <fqdn>[:<port>] [params*]
 
     <port>    Same meaning as "server" <port> argument (see "server" keyword).
 
+    <origin>  instructs to reuse the settings from the designated server or
+              default-server instance. See section 5.2 about server options for
+              a complete description of the supported arguments.
+
     <params*>
               Remaining server parameters among all those supported by "server"
               keyword.
@@ -18168,16 +18177,24 @@ which are all passed as arguments on the server line. The order in which those
 arguments appear does not count, and they are all optional. Some of those
 settings are single words (booleans) while others expect one or several values
 after them. In this case, the values must immediately follow the setting name.
+
 Except default-server, all those settings must be specified after the server's
-address if they are used:
+address if they are used. Before any of those settings, the extra keyword
+"from" may be used both on "server" and "default-server" lines to preset
+its settings.
 
-  server <name> <address>[:port] [settings ...]
-  default-server [settings ...]
+  server <name> <address>[:port] [from <origin>] [settings ...]
+  default-server [from <origin>] [settings ...]
 
-Note that all these settings are supported both by "server" and "default-server"
-keywords, except "id" which is only supported by "server".
+from <origin>
+  Preinitialize the server settings by copying values from another server or
+  default-server instance.
 
-The currently supported settings are the following ones.
+  Here is the list of the supported values for the "from" keyword :
+
+The currently supported server settings are the following ones. Note that all
+these settings are supported both by "server" and "default-server" keywords,
+except "id" which is only supported by "server".
 
 addr <ipv4|ipv6>
   May be used in the following contexts: tcp, http, log
diff --git a/reg-tests/server/from_keyword.vtc b/reg-tests/server/from_keyword.vtc
new file mode 100644 (file)
index 0000000..cf48f2f
--- /dev/null
@@ -0,0 +1,48 @@
+varnishtest "Test server from keyword"
+
+feature ignore_unknown_macro
+
+# Do nothing. Is there only to create s1_* macros
+server s1 {
+       rxreq
+       txresp
+} -start
+
+haproxy h1 -conf {
+       global
+       .if feature(THREAD)
+               thread-groups 1
+       .endif
+
+       defaults
+               mode http
+               timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+       frontend fe
+               bind "fd@${feS}"
+               use_backend be
+
+       backend be
+               default-server weight 10
+               # ensure that default-server overloading is working as expected
+               default-server inter 5s
+
+               server srvconf1 ${s1_addr}:${s1_port}
+} -start
+
+haproxy h1 -cli {
+       # static server uses by default the default-server
+       send "get weight be/srvconf1"
+       expect ~ "10 \\(initial 10\\)"
+}
+
+# dynamic servers
+haproxy h1 -cli {
+       # dynamic server does not use the default-server by default
+       send "add server be/srvdyn1 ${s1_addr}:${s1_port}"
+       expect ~ "New server registered."
+       send "get weight be/srvdyn1"
+       expect ~ "1 \\(initial 1\\)"
+}
index 5e8bc70702c140d4df9920a3ce00cc87a8799919..ec86c30b1b57c81d98e8c389cb0875641d7e8fc9 100644 (file)
@@ -3888,11 +3888,6 @@ static int _srv_parse_init(struct server **srv, char **args, int *cur_arg,
 
                (*cur_arg)++;
  skip_addr:
-               if (!(parse_flags & SRV_PARSE_DYNAMIC)) {
-                       /* Copy default server settings to new server */
-                       srv_settings_cpy(newsrv, curproxy->defsrv, 0);
-               } else
-                       srv_settings_init(newsrv);
                HA_SPIN_INIT(&newsrv->lock);
        }
        else {
@@ -3924,6 +3919,66 @@ out:
        return err_code;
 }
 
+/* Try to parse optional positional "from" keyword for <srv> server instance.
+ * The keyword is read from <args>. If found <cur_arg> is incremented to the
+ * next argument.
+ *
+ * On return, <from> will point to a server or default-instance from with
+ * settings must be copied. If NULL the server settings must be initialized to
+ * default clean values. If <from> points to <srv>, the caller must neither
+ * reinit or copy settings.
+ *
+ * A mask of errors is returned. ERR_FATAL is set on parsing error.
+ */
+static int _srv_parse_from(struct server *srv, char **args, int *cur_arg,
+                           struct proxy *curproxy, struct server **from,
+                           int parse_flags)
+{
+       int err_code = ERR_NONE;
+
+       if (strcmp(args[*cur_arg], "from") == 0) {
+               if (!*args[*cur_arg + 1]) {
+                       ha_alert("from: missing value.\n");
+                       err_code |= ERR_FATAL | ERR_ALERT;
+                       goto out;
+               }
+               else {
+                       ha_alert("invalid '%s' value for 'from' keyword.\n", args[*cur_arg + 1]);
+                       err_code |= ERR_FATAL | ERR_ALERT;
+                       goto out;
+               }
+
+               *cur_arg += 2;
+
+               /* detect duplicate 'from' keyword usage. */
+               if (strcmp(args[*cur_arg], "from") == 0) {
+                       ha_alert("'from' keyword can only be specified once.\n");
+                       err_code |= ERR_FATAL | ERR_ALERT;
+                       goto out;
+               }
+       }
+       else {
+               /* from keyword not used : fallback to the default behavior. */
+               if (parse_flags & SRV_PARSE_DEFAULT_SERVER) {
+                       /* default-server can be defined on multiple lines with settings overriding.
+                        * In this case, caller do not have to reinit or copy the settings.
+                        */
+                       *from = srv;
+               }
+               else if (!(parse_flags & SRV_PARSE_DYNAMIC)) {
+                       /* Reuses the default-server in the current proxy when parsing configuration files. */
+                       *from = curproxy->defsrv;
+               }
+               else {
+                       /* Servers added at runtime to not inherit by default from the default-server. */
+                       *from = NULL;
+               }
+       }
+
+ out:
+       return err_code;
+}
+
 /* Parse the server keyword in <args>.
  * <cur_arg> is incremented beyond the keyword optional value. Note that this
  * might not be the case if an error is reported.
@@ -4113,6 +4168,7 @@ int parse_server(const char *file, int linenum, char **args,
                  int parse_flags)
 {
        struct server *newsrv = NULL;
+       struct server *from = NULL;
        int err_code = 0;
 
        int cur_arg;
@@ -4137,10 +4193,19 @@ int parse_server(const char *file, int linenum, char **args,
 
        err_code = _srv_parse_init(&newsrv, args, &cur_arg, curproxy,
                                   parse_flags);
-
        if (err_code & ERR_CODE)
                goto out;
 
+       err_code = _srv_parse_from(newsrv, args, &cur_arg, curproxy, &from,
+                                  parse_flags);
+       if (err_code & ERR_FATAL)
+               goto out;
+
+       if (newsrv != from) {
+               /* This will copy <from> settings or init them if NULL. */
+               srv_settings_cpy(newsrv, from, !!(parse_flags & SRV_PARSE_TEMPLATE));
+       }
+
        if (!newsrv->conf.file) // note: do it only once for default-server
                newsrv->conf.file = strdup(file);
        newsrv->conf.line = linenum;
@@ -6366,7 +6431,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
 {
        struct add_srv_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
        struct proxy *be;
-       struct server *srv;
+       struct server *srv, *from = NULL;
        char *be_name, *sv_name, *errmsg;
        int errcode, argc;
        const int parse_flags = SRV_PARSE_DYNAMIC|SRV_PARSE_PARSE_ADDR;
@@ -6430,6 +6495,11 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
        if (errcode)
                goto out;
 
+       errcode = _srv_parse_from(srv, args, &argc, be, &from, parse_flags);
+       if (errcode)
+               goto out;
+       srv_settings_cpy(srv, from, 0);
+
        while (*args[argc]) {
                errcode = _srv_parse_kw(srv, args, &argc, be, parse_flags);