]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: checks: Implement redis check using tcp-check rules
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 1 Apr 2020 09:08:50 +0000 (11:08 +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 redis checks. This way no
extra memory is used if several backends use a redis check.

The following sequence is used :

  tcp-check send "*1\r\n$4\r\nPING\r\n"

  tcp-check expect string "+PONG\r\n" error-status "L7STS" \
      on-error "%[check.payload(),cut_crlf]" on-success "Redis server is ok"

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

index 43d287d76e092d9198c8b8e0d5377044b40cf225..d52e00580cc20a320d46109340460c877f528503 100644 (file)
 #define DEF_CHECK_PATH  ""
 #define DEF_SMTP_CHECK_REQ   "HELO localhost\r\n"
 #define DEF_LDAP_CHECK_REQ   "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00"
-#define DEF_REDIS_CHECK_REQ  "*1\r\n$4\r\nPING\r\n"
+
 
 #define DEF_HANA_ONERR         HANA_ONERR_FAILCHK
 #define DEF_HANA_ERRLIMIT      10
index d0c827bd77a5bd0b9e769631752d001b7bd92028..1c76a896845ccc3654c6a7c9394ad601b0608d12 100644 (file)
@@ -69,6 +69,8 @@ int dup_tcpcheck_vars(struct list *dst, struct list *src);
 int spoe_prepare_healthcheck_request(char **req, int *len);
 int spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen);
 
+int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                               const char *file, int line);
 #endif /* _PROTO_CHECKS_H */
 
 /*
index 30f7d75fb044721c666daea6f30a2cf854cc5332..b5f4649e226127464ce01ac932ea1b9f0176bd79 100644 (file)
@@ -308,9 +308,11 @@ struct tcpcheck_rule {
        };
 };
 
-#define TCPCHK_RULES_NONE   0x00000000
-#define TCPCHK_RULES_SHARED 0x00000001 /* Set for shared list of tcp-check rules */
-#define TCPCHK_RULES_DEF    0x00000002 /* Ruleset inherited from the default section */
+#define TCPCHK_RULES_NONE        0x00000000
+#define TCPCHK_RULES_SHARED      0x00000001 /* Set for shared list of tcp-check rules */
+#define TCPCHK_RULES_DEF         0x00000002 /* Ruleset inherited from the default section */
+
+#define TCPCHK_RULES_REDIS_CHK   0x00000020
 
 /* A list of tcp-check vars, to be registered before executing a ruleset */
 struct tcpcheck_var {
index 270445a44d25cea9e90c433bec948d0a3f86def9..792c25225ce2553a941c03b2400e4e539f59baa9 100644 (file)
@@ -170,7 +170,7 @@ enum PR_SRV_STATE_FILE {
 /* server health checks */
 #define PR_O2_CHK_NONE  0x00000000      /* no L7 health checks configured (TCP by default) */
 #define PR_O2_PGSQL_CHK 0x10000000      /* use PGSQL check for server health */
-#define PR_O2_REDIS_CHK 0x20000000      /* use LDAP check for server health */
+/* unused: 0x20000000 */
 #define PR_O2_SMTP_CHK  0x30000000      /* use SMTP EHLO check for server health - pvandijk@vision6.com.au */
 #define PR_O2_HTTP_CHK  0x40000000      /* use HTTP 'OPTIONS' method to check server health */
 #define PR_O2_MYSQL_CHK 0x50000000      /* use MYSQL check for server health */
index 0b2ef1583c0c1e1e9be4d81653e28fe441cf959a..fdcf68e83111281a464f269adfd4b08b16c50e86 100644 (file)
@@ -2495,22 +2495,9 @@ stats_error_parsing:
                        if (alertif_too_many_args_idx(2, 1, file, linenum, args, &err_code))
                                goto out;
                }
-
                else if (!strcmp(args[1], "redis-check")) {
-                       /* use REDIS PING request to check servers' health */
-                       if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL))
-                               err_code |= ERR_WARN;
-
-                       free(curproxy->check_req);
-                       curproxy->check_req = NULL;
-                       curproxy->options2 &= ~PR_O2_CHK_ANY;
-                       curproxy->options2 |= PR_O2_REDIS_CHK;
-
-                       curproxy->check_req = malloc(sizeof(DEF_REDIS_CHECK_REQ) - 1);
-                       memcpy(curproxy->check_req, DEF_REDIS_CHECK_REQ, sizeof(DEF_REDIS_CHECK_REQ) - 1);
-                       curproxy->check_len = sizeof(DEF_REDIS_CHECK_REQ) - 1;
-
-                       if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
+                       err_code |= proxy_parse_redis_check_opt(args, 0, curproxy, &defproxy, file, linenum);
+                       if (err_code & ERR_FATAL)
                                goto out;
                }
 
index c6bced88a65500997bd80c115f470e77866a9870..c7f156becba3dfdc92ad3b0fa430f5a26a9e1552 100644 (file)
@@ -1222,18 +1222,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
                }
                break;
 
-       case PR_O2_REDIS_CHK:
-               if (!done && b_data(&check->bi) < 7)
-                       goto wait_more_data;
-
-               if (strcmp(b_head(&check->bi), "+PONG\r\n") == 0) {
-                       set_server_check_status(check, HCHK_STATUS_L7OKD, "Redis server is ok");
-               }
-               else {
-                       set_server_check_status(check, HCHK_STATUS_L7STS, b_head(&check->bi));
-               }
-               break;
-
        case PR_O2_MYSQL_CHK:
                if (!done && b_data(&check->bi) < 5)
                        goto wait_more_data;
@@ -5077,6 +5065,135 @@ static int proxy_parse_tcpcheck(char **args, int section, struct proxy *curpx,
        return -1;
 }
 
+
+static struct tcpcheck_ruleset *tcpcheck_ruleset_lookup(const char *name)
+{
+       struct tcpcheck_ruleset *rs;
+
+       list_for_each_entry(rs, &tcpchecks_list, list) {
+               if (strcmp(rs->name, name) == 0)
+                       return rs;
+       }
+       return NULL;
+}
+
+static struct tcpcheck_ruleset *tcpcheck_ruleset_create(const char *name)
+{
+       struct tcpcheck_ruleset *rs;
+
+       rs = calloc(1, sizeof(*rs));
+       if (rs == NULL)
+               return NULL;
+
+       rs->name = strdup(name);
+       if (rs->name == NULL) {
+               free(rs);
+               return NULL;
+       }
+
+       LIST_INIT(&rs->list);
+       LIST_INIT(&rs->rules);
+       LIST_ADDQ(&tcpchecks_list, &rs->list);
+       return rs;
+}
+
+static void tcpcheck_ruleset_release(struct tcpcheck_ruleset *rs)
+{
+       struct tcpcheck_rule *r, *rb;
+       if (!rs)
+               return;
+
+       LIST_DEL(&rs->list);
+       list_for_each_entry_safe(r, rb, &rs->rules, list) {
+               LIST_DEL(&r->list);
+               free_tcpcheck(r, 0);
+       }
+       free(rs->name);
+       free(rs);
+}
+
+
+/* Parses the "option redis-check" proxy keyword */
+int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+                               const char *file, int line)
+{
+       static char *redis_req = "*1\r\n$4\r\nPING\r\n";
+       static char *redis_res = "+PONG\r\n";
+
+       struct tcpcheck_ruleset *rs = NULL;
+       struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+       struct tcpcheck_rule *chk;
+       char *errmsg = NULL;
+       int 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("*redis-check");
+       if (rs)
+               goto ruleset_found;
+
+       rs = tcpcheck_ruleset_create("*redis-check");
+       if (rs == NULL) {
+               ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+               goto error;
+       }
+
+       chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
+                                 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", "string", redis_res,
+                                              "error-status", "L7STS",
+                                              "on-error", "%[check.payload(),cut_crlf]",
+                                              "on-success", "Redis server is ok",
+                                              ""},
+                                   1, curpx, &rs->rules, file, line, &errmsg);
+       if (!chk) {
+               ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+               goto error;
+       }
+       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_REDIS_CHK);
+
+  out:
+       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 },