From: Christopher Faulet Date: Wed, 1 Apr 2020 09:08:50 +0000 (+0200) Subject: MEDIUM: checks: Implement redis check using tcp-check rules X-Git-Tag: v2.2-dev7~128 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33f05df650b6b35ac2e3b2208ffea6aa3131a40b;p=thirdparty%2Fhaproxy.git MEDIUM: checks: Implement redis check using tcp-check rules 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" --- diff --git a/include/common/defaults.h b/include/common/defaults.h index 43d287d76e..d52e00580c 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -179,7 +179,7 @@ #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 diff --git a/include/proto/checks.h b/include/proto/checks.h index d0c827bd77..1c76a89684 100644 --- a/include/proto/checks.h +++ b/include/proto/checks.h @@ -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 */ /* diff --git a/include/types/checks.h b/include/types/checks.h index 30f7d75fb0..b5f4649e22 100644 --- a/include/types/checks.h +++ b/include/types/checks.h @@ -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 { diff --git a/include/types/proxy.h b/include/types/proxy.h index 270445a44d..792c25225c 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -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 */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 0b2ef1583c..fdcf68e831 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -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; } diff --git a/src/checks.c b/src/checks.c index c6bced88a6..c7f156becb 100644 --- a/src/checks.c +++ b/src/checks.c @@ -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 },