]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: checks: Implement SPOP check using tcp-check rules
authorChristopher Faulet <cfaulet@haproxy.com>
Sat, 4 Apr 2020 08:27:09 +0000 (10:27 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 27 Apr 2020 07:39:38 +0000 (09:39 +0200)
A share tcp-check ruleset is now created to support SPOP checks. This way no
extra memory is used if several backends use a SPOP check.

The following sequence is used :

    tcp-check send-binary SPOP_REQ
    tcp-check expect custom min-recv 4

The spop request is the result of the function
spoe_prepare_healthcheck_request() and the expect rule relies on a custom
function calling spoe_handle_healthcheck_response().

include/proto/checks.h
include/types/checks.h
include/types/proxy.h
src/cfgparse-listen.c
src/checks.c

index ff432384a4d5d09e1ab1b2c5aa5b2307e51495dc..669c62080183371dcf7814ca8e27cb12b69a0f34 100644 (file)
@@ -81,6 +81,8 @@ int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, s
                            const char *file, int line);
 int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
                               const char *file, int line);
+int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                              const char *file, int line);
 
 #endif /* _PROTO_CHECKS_H */
 
index a5676c18ae321376d8db26129fcad5927cbb4ab1..d3da5f29adc669a92c87754bd1ae6f858f2f106f 100644 (file)
@@ -318,6 +318,7 @@ struct tcpcheck_rule {
 #define TCPCHK_RULES_MYSQL_CHK   0x00000050
 #define TCPCHK_RULES_LDAP_CHK    0x00000060
 #define TCPCHK_RULES_SSL3_CHK    0x00000070
+#define TCPCHK_RULES_SPOP_CHK    0x00000090
 
 /* A list of tcp-check vars, to be registered before executing a ruleset */
 struct tcpcheck_var {
index bf2a798b33d62f7b6fd91ccb3694b6d1fb70ffdd..e13c63c05502e6c9dbf468af8dfe1cf81ff89849 100644 (file)
@@ -175,8 +175,7 @@ enum PR_SRV_STATE_FILE {
 #define PR_O2_LB_AGENT_CHK 0x80000000   /* use a TCP connection to obtain a metric of server health */
 #define PR_O2_TCPCHK_CHK 0x90000000     /* use TCPCHK check for server health */
 #define PR_O2_EXT_CHK   0xA0000000      /* use external command for server health */
-#define PR_O2_SPOP_CHK  0xB0000000      /* use SPOP for server health */
-/* unused: 0xC0000000 to 0xF000000, reserved for health checks */
+/* unused: 0xB0000000 to 0xF000000, reserved for health checks */
 #define PR_O2_CHK_ANY   0xF0000000      /* Mask to cover any check */
 /* end of proxy->options2 */
 
index 42867425876703ac09612042cac03ef1f8a69019..1111b743f46fb5c479393babc382d953619bf9dc 100644 (file)
@@ -2427,31 +2427,8 @@ stats_error_parsing:
                                goto out;
                }
                else if (!strcmp(args[1], "spop-check")) {
-                       if (curproxy == &defproxy) {
-                               ha_alert("parsing [%s:%d] : '%s %s' not allowed in 'defaults' section.\n",
-                                        file, linenum, args[0], args[1]);
-                               err_code |= ERR_ALERT | ERR_FATAL;
-                               goto out;
-                       }
-                       if (curproxy->cap & PR_CAP_FE) {
-                               ha_alert("parsing [%s:%d] : '%s %s' not allowed in 'frontend' and 'listen' sections.\n",
-                                        file, linenum, args[0], args[1]);
-                               err_code |= ERR_ALERT | ERR_FATAL;
-                               goto out;
-                       }
-
-                       /* use SPOE request to check servers' health */
-                       free(curproxy->check_req);
-                       curproxy->check_req = NULL;
-                       curproxy->options2 &= ~PR_O2_CHK_ANY;
-                       curproxy->options2 |= PR_O2_SPOP_CHK;
-
-                       if (spoe_prepare_healthcheck_request(&curproxy->check_req, &curproxy->check_len)) {
-                               ha_alert("parsing [%s:%d] : failed to prepare SPOP healthcheck request.\n", file, linenum);
-                               err_code |= ERR_ALERT | ERR_FATAL;
-                               goto out;
-                       }
-                       if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
+                       err_code |= proxy_parse_spop_check_opt(args, 0, curproxy, &defproxy, file, linenum);
+                       if (err_code & ERR_FATAL)
                                goto out;
                }
                else if (!strcmp(args[1], "tcp-check")) {
index fa595ea6593e63e669b582ec350f9947b6eb561e..dc09a19fd5cf192398bae610040a937d40cbf390 100644 (file)
@@ -1155,26 +1155,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
                break;
        }
 
-       case PR_O2_SPOP_CHK: {
-               unsigned int framesz;
-               char         err[HCHK_DESC_LEN];
-
-               if (!done && b_data(&check->bi) < 4)
-                       goto wait_more_data;
-
-               memcpy(&framesz, b_head(&check->bi), 4);
-               framesz = ntohl(framesz);
-
-               if (!done && b_data(&check->bi) < (4+framesz))
-                   goto wait_more_data;
-
-               if (!spoe_handle_healthcheck_response(b_head(&check->bi)+4, framesz, err, HCHK_DESC_LEN-1))
-                       set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok");
-               else
-                       set_server_check_status(check, HCHK_STATUS_L7STS, err);
-               break;
-       }
-
        default:
                /* good connection is enough for pure TCP check */
                if (!(conn->flags & CO_FL_WAIT_XPRT) && !check->type) {
@@ -2782,6 +2762,48 @@ static enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check,
        goto out;
 }
 
+
+static enum tcpcheck_eval_ret tcpcheck_spop_expect_agenthello(struct check *check, struct tcpcheck_rule *rule, int last_read)
+{
+       enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
+       enum healthcheck_status status;
+       struct buffer *msg = NULL;
+       struct ist desc = ist(NULL);
+       unsigned int framesz;
+
+
+       memcpy(&framesz, b_head(&check->bi), 4);
+       framesz = ntohl(framesz);
+
+       if (!last_read && b_data(&check->bi) < (4+framesz))
+               goto wait_more_data;
+
+       memset(b_orig(&trash), 0, b_size(&trash));
+       if (spoe_handle_healthcheck_response(b_peek(&check->bi, 4), framesz, b_orig(&trash), HCHK_DESC_LEN) == -1) {
+               status = HCHK_STATUS_L7RSP;
+               desc = ist2(b_orig(&trash), strlen(b_orig(&trash)));
+               goto error;
+       }
+
+       set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok");
+
+  out:
+       free_trash_chunk(msg);
+       return ret;
+
+  error:
+       ret = TCPCHK_EVAL_STOP;
+       msg = alloc_trash_chunk();
+       if (msg)
+               tcpcheck_onerror_message(msg, check, rule, 0, desc);
+       set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
+       goto out;
+
+  wait_more_data:
+       ret = TCPCHK_EVAL_WAIT;
+       goto out;
+}
+
 /* Evaluate a TCPCHK_ACT_CONNECT rule. It returns 1 to evaluate the next rule, 0
  * to wait and -1 to stop the check. */
 static enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpcheck_rule *rule)
@@ -5862,6 +5884,91 @@ int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, st
        goto out;
 }
 
+int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                              const char *file, int line)
+{
+       struct tcpcheck_ruleset *rs = NULL;
+       struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+       struct tcpcheck_rule *chk;
+       char *spop_req = NULL;
+       char *errmsg = NULL;
+       int spop_len = 0, err_code = 0;
+
+       if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+               err_code |= ERR_WARN;
+
+       if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+               goto out;
+
+       if (rules->list && !(rules->flags & TCPCHK_RULES_SHARED)) {
+               ha_alert("parsing [%s:%d] : A custom tcp-check ruleset is already configured.\n",
+                        file, line);
+               err_code |= ERR_ALERT | ERR_FATAL;
+               goto out;
+       }
+
+       curpx->options2 &= ~PR_O2_CHK_ANY;
+       curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+       free_tcpcheck_vars(&rules->preset_vars);
+       rules->list  = NULL;
+       rules->flags = 0;
+
+
+       rs = tcpcheck_ruleset_lookup("*spop-check");
+       if (rs)
+               goto ruleset_found;
+
+       rs = tcpcheck_ruleset_create("*spop-check");
+       if (rs == NULL) {
+               ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+               goto error;
+       }
+
+       if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
+               ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+               goto error;
+       }
+       chunk_reset(&trash);
+       dump_binary(&trash, spop_req, spop_len);
+       trash.area[trash.data] = '\0';
+
+       chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
+                                 1, curpx, &rs->rules, file, line, &errmsg);
+       if (!chk) {
+               ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+               goto error;
+       }
+       chk->index = 0;
+       LIST_ADDQ(&rs->rules, &chk->list);
+
+       chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
+                                   1, curpx, &rs->rules, file, line, &errmsg);
+       if (!chk) {
+               ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+               goto error;
+       }
+       chk->expect.custom = tcpcheck_spop_expect_agenthello;
+       chk->index = 1;
+       LIST_ADDQ(&rs->rules, &chk->list);
+
+       LIST_ADDQ(&tcpchecks_list, &rs->list);
+
+  ruleset_found:
+       rules->list = &rs->rules;
+       rules->flags |= (TCPCHK_RULES_SHARED|TCPCHK_RULES_SPOP_CHK);
+
+  out:
+       free(spop_req);
+       free(errmsg);
+       return err_code;
+
+  error:
+       tcpcheck_ruleset_release(rs);
+       err_code |= ERR_ALERT | ERR_FATAL;
+       goto out;
+}
+
 static struct cfg_kw_list cfg_kws = {ILH, {
         { CFG_LISTEN, "tcp-check",  proxy_parse_tcpcheck },
         { 0, NULL, NULL },