]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tcpcheck/server: Add healthcheck server keyword
authorChristopher Faulet <cfaulet@haproxy.com>
Fri, 27 Mar 2026 15:24:16 +0000 (16:24 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 1 Apr 2026 14:34:38 +0000 (16:34 +0200)
Thanks to this patch, it is now possible to specify an healthcheck section
on the server line. In that case, the server will use the tcpcheck as
defined in the correspoding healthcheck section instead of the proxy's one.

doc/configuration.txt
include/haproxy/tcpcheck-t.h
src/check.c
src/proxy.c
src/tcpcheck.c

index 474c35b301cf29c7d47618803a6669c26c16eb94..7d6b1d86e35de445b0de4b6b9e5eb37bde14be7f 100644 (file)
@@ -18673,6 +18673,18 @@ hash-key <key>
   uses "hash-type consistent", and the quality of the distribution will depend
   on the quality of the keys.
 
+healthchec <name>
+  May be used in the following contexts: tcp, http
+
+  Specify the health-check sectino to use to perform check on the server.
+
+  Argument :
+    <name>    is the health-check section name.
+
+  Thanks to this option, it is possible to use a pre-server health-check
+  configuration instead of using the proxy configuration. See also "healthcheck
+  section".
+
 id <value>
   May be used in the following contexts: tcp, http, log
 
@@ -32364,6 +32376,81 @@ Example:
       map virt@acme
 
 
+12.9. Healthchecks
+------------------
+
+It is possible to globally declare several health-checks that could be used by
+servers across all the configuration, overriding the local proxy configuration.
+
+healthcheck <name>
+  Created a new healthcheck with name <name>. This name must be unique. It
+  should be used on server line to reference a specific health-check section.
+
+type <type>
+  Defines the health-check type. This parameter is mandatory. Following types
+  of health-check are supported:
+
+    * tcp-check
+    * httpchk
+    * ssl-hello-chk
+    * smtpchk
+    * pgsql-check
+    * redis-check
+    * mysql-check
+    * ldap-check
+    * spop-check
+
+  Each type uses the same parameters, if any, than the corresponding proxy's
+  option. For instance, the method, the uri... may be speficied for the
+  "httpchk" type:
+
+  Examples :
+        healthcheck my-http-check
+           type httpchk GET /health HTTP/1.1 %[srv_name]
+
+  See also : "option tcp-check", "option httpchk", "option ssl-hello-chk",
+             "option smtpchk", "option mysql-check", "option pgsql-check",
+            "option redis-check", "option ldap-check and "option spop-check"
+
+http-check comment <string>
+http-check connect [default] [port <expr>] [addr <ip>] [send-proxy]
+                   [via-socks4] [ssl] [sni <sni>] [alpn <alpn>] [linger]
+                   [proto <name>] [comment <msg>]
+http-check disable-on-404
+http-check expect [min-recv <int>] [comment <msg>]
+                  [ok-status <st>] [error-status <st>] [tout-status <st>]
+                  [on-success <fmt>] [on-error <fmt>] [status-code <expr>]
+                  [!] <match> <pattern>
+http-check send [meth <method>] [{ uri <uri> | uri-lf <fmt> }>] [ver <version>]
+                [hdr <name> <fmt>]* [{ body <string> | body-lf <fmt> }]
+                [comment <msg>]
+http-check send-state
+http-check set-var(<var-name>[,<cond>...]) <expr>
+http-check set-var-fmt(<var-name>[,<cond>...]) <fmt>
+http-check unset-var(<var-name>)
+  Add a specific http-check rule for a "httpchk" healthcheck. The same syntax
+  than the corresponding proxy's directives is used. See the corresponding proxy
+  documentation for details.
+
+tcp-check comment <string>
+tcp-check connect [default] [port <expr>] [addr <ip>] [send-proxy] [via-socks4]
+                  [ssl] [sni <sni>] [alpn <alpn>] [linger]
+                  [proto <name>] [comment <msg>]
+tcp-check expect [min-recv <int>] [comment <msg>]
+                 [ok-status <st>] [error-status <st>] [tout-status <st>]
+                 [on-success <fmt>] [on-error <fmt>] [status-code <expr>]
+                 [!] <match> <pattern>
+tcp-check send <data> [comment <msg>]
+tcp-check send-lf <fmt> [comment <msg>]
+tcp-check send-binary <hexstring> [comment <msg>]
+tcp-check send-binary-lf <hexfmt> [comment <msg>]
+tcp-check set-var(<var-name>[,<cond>...]) <expr>
+tcp-check set-var-fmt(<var-name>[,<cond>...]) <fmt>
+tcp-check unset-var(<var-name>)
+  Add a specific tcp-check rule for a "tcp-check" healthcheck. The same syntax
+  than the corresponding proxy's directives is used. See the corresponding proxy
+  documentation for details.
+
 /*
  * Local variables:
  *  fill-column: 79
index 6ff78ffc5270de1d6e9f42c29263975f25825e63..dec0c90b495fc438ac87b5ebefdbc99635cab285 100644 (file)
@@ -248,6 +248,7 @@ struct tcpcheck_ruleset {
 
 struct tcpcheck {
        struct tcpcheck_ruleset *rs; /* The tcp-check ruleset to use */
+       char *healthcheck;           /* name of the healthcheck section (NULL if not used) */
        struct list preset_vars;     /* The list of variable to preset before executing the ruleset */
        unsigned int flags;          /* TCPCHECK_FL_* */
 };
index b13a9f8ee4e29bacd9f37d2d055c8eaf0e9fd4ea..6a29ebe5728e3c05569438179e476f9d8aecd721 100644 (file)
@@ -1552,8 +1552,9 @@ void free_check(struct check *check)
         * done for health-check : the proxy is the owner of the rules / vars
         * in this case.
         */
-       if (check->state & CHK_ST_AGENT) {
+       if (check->state & CHK_ST_AGENT || check->tcpcheck->healthcheck) {
                free_tcpcheck_vars(&check->tcpcheck->preset_vars);
+               ha_free(&check->tcpcheck->healthcheck);
                ha_free(&check->tcpcheck);
        }
 
index c028e348c6f62f7f45383853025c354421b9f3fd..8dd10fb6f9f1a08ada6d21c6d7ba08a4b5fde692 100644 (file)
@@ -2537,6 +2537,10 @@ int proxy_finalize(struct proxy *px, int *err_code)
 
                srv_minmax_conn_apply(newsrv);
 
+               *err_code |= check_server_tcpcheck(newsrv);
+               if (*err_code & (ERR_ABORT|ERR_FATAL))
+                       goto out;
+
                /* this will also properly set the transport layer for
                 * prod and checks
                 * if default-server have use_ssl, prerare ssl init
index ed604609b0a4ef314c0d5dc8e2e8045a2044bb2f..c88705289726211d72ed2d67dcafe5c4dda37ec9 100644 (file)
@@ -4164,6 +4164,35 @@ static int check_proxy_tcpcheck(struct proxy *px)
        return ret;
 }
 
+int check_server_tcpcheck(struct server *srv)
+{
+       struct tcpcheck_ruleset *rs;
+       int err_code = 0;
+
+       if (srv->check.tcpcheck->healthcheck) {
+               rs = find_tcpcheck_ruleset(srv->check.tcpcheck->healthcheck);
+               if (!rs) {
+                       ha_alert("parsing [%s:%d]: healthcheck section '%s' not found for server '%s'.\n",
+                                srv->conf.file, srv->conf.line, srv->check.tcpcheck->healthcheck, srv->id);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               if (!dup_tcpcheck_vars(&srv->check.tcpcheck->preset_vars, &rs->conf.preset_vars)) {
+                       ha_alert("parsing [%s:%d]:  unable to duplicate preset variables for server '%s'.\n",
+                                srv->conf.file, srv->conf.line, srv->id);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               srv->check.tcpcheck->rs = rs;
+               srv->check.tcpcheck->flags = rs->conf.flags;
+
+               err_code = check_tcpcheck_ruleset(srv->proxy, rs);
+       }
+
+  out:
+       return err_code;
+}
+
 void deinit_proxy_tcpcheck(struct proxy *px)
 {
        free_tcpcheck_vars(&px->tcpcheck.preset_vars);
@@ -5920,6 +5949,43 @@ int cfg_post_parse_healthchecks()
        return err_code;
 }
 
+/* Parse the "healthcheck" server keyword */
+static int srv_parse_healthcheck(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
+                                char **errmsg)
+{
+       int err_code = 0;
+
+       if (!*args[*cur_arg+1]) {
+               memprintf(errmsg, "'%s' expects a healthcheck name as argument.", args[*cur_arg]);
+               err_code |= ERR_ALERT | ERR_ABORT;
+               goto out;
+       }
+
+       if (srv->check.tcpcheck->healthcheck) {
+               /* a healthcheck section was already defined. Replace it */
+               ha_free(&srv->check.tcpcheck->healthcheck);
+       }
+       else {
+               srv->check.tcpcheck = calloc(1, sizeof(*srv->check.tcpcheck));
+               if (srv->check.tcpcheck == NULL) {
+                       memprintf(errmsg, "out of memory");
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               LIST_INIT(&srv->check.tcpcheck->preset_vars);
+       }
+
+       if (!memprintf(&srv->check.tcpcheck->healthcheck, "*healthcheck-%s", args[*cur_arg+1])) {
+               ha_free(&srv->check.tcpcheck);
+               memprintf(errmsg, "out of memory");
+               err_code |= ERR_ALERT | ERR_FATAL;
+               goto out;
+       }
+
+  out:
+       return err_code;
+}
+
 static struct cfg_kw_list cfg_kws = {ILH, {
         { CFG_LISTEN, "http-check",     proxy_parse_httpcheck },
         { CFG_LISTEN, "tcp-check",      proxy_parse_tcpcheck },
@@ -5932,3 +5998,11 @@ REGISTER_POST_DEINIT(deinit_tcpchecks);
 INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
 
 REGISTER_CONFIG_SECTION("healthcheck",  cfg_parse_healthchecks,  cfg_post_parse_healthchecks);
+
+/* register "server" line keywords */
+static struct srv_kw_list srv_kws = { "CHK", { }, {
+       { "healthcheck", srv_parse_healthcheck,  1,  1,  1 }, /* The healthcheck section to use */
+       { NULL, NULL, 0 },
+}};
+
+INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);