From b49200ca31b366d9cc2dee4e21ef26717ee933bb Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Fri, 27 Mar 2026 16:24:16 +0100 Subject: [PATCH] MEDIUM: tcpcheck/server: Add healthcheck server keyword 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 | 87 ++++++++++++++++++++++++++++++++++++ include/haproxy/tcpcheck-t.h | 1 + src/check.c | 3 +- src/proxy.c | 4 ++ src/tcpcheck.c | 74 ++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 3e2278b92..44b32d50a 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18561,6 +18561,18 @@ hash-key uses "hash-type consistent", and the quality of the distribution will depend on the quality of the keys. +healthchec + May be used in the following contexts: tcp, http + + Specify the health-check sectino to use to perform check on the server. + + Argument : + 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 May be used in the following contexts: tcp, http, log @@ -32251,6 +32263,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 + Created a new healthcheck with name . This name must be unique. It + should be used on server line to reference a specific health-check section. + +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 +http-check connect [default] [port ] [addr ] [send-proxy] + [via-socks4] [ssl] [sni ] [alpn ] [linger] + [proto ] [comment ] +http-check disable-on-404 +http-check expect [min-recv ] [comment ] + [ok-status ] [error-status ] [tout-status ] + [on-success ] [on-error ] [status-code ] + [!] +http-check send [meth ] [{ uri | uri-lf }>] [ver ] + [hdr ]* [{ body | body-lf }] + [comment ] +http-check send-state +http-check set-var([,...]) +http-check set-var-fmt([,...]) +http-check unset-var() + 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 +tcp-check connect [default] [port ] [addr ] [send-proxy] [via-socks4] + [ssl] [sni ] [alpn ] [linger] + [proto ] [comment ] +tcp-check expect [min-recv ] [comment ] + [ok-status ] [error-status ] [tout-status ] + [on-success ] [on-error ] [status-code ] + [!] +tcp-check send [comment ] +tcp-check send-lf [comment ] +tcp-check send-binary [comment ] +tcp-check send-binary-lf [comment ] +tcp-check set-var([,...]) +tcp-check set-var-fmt([,...]) +tcp-check unset-var() + 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 diff --git a/include/haproxy/tcpcheck-t.h b/include/haproxy/tcpcheck-t.h index 6ff78ffc5..dec0c90b4 100644 --- a/include/haproxy/tcpcheck-t.h +++ b/include/haproxy/tcpcheck-t.h @@ -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_* */ }; diff --git a/src/check.c b/src/check.c index b13a9f8ee..6a29ebe57 100644 --- a/src/check.c +++ b/src/check.c @@ -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); } diff --git a/src/proxy.c b/src/proxy.c index c028e348c..8dd10fb6f 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -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 diff --git a/src/tcpcheck.c b/src/tcpcheck.c index ed604609b..c88705289 100644 --- a/src/tcpcheck.c +++ b/src/tcpcheck.c @@ -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);