mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-28 16:40:37 +00:00
MEDIUM: checks: Implement postgres check using tcp-check rules
A shared tcp-check ruleset is now created to support postgres check. This way no extra memory is used if several backends use a pgsql check. The following sequence is used : tcp-check connect default linger tcp-check send-binary PGSQL_REQ log-format tcp-check expect !rstring "^E" min-recv 5 \ error-status "L7RSP" on-error "%[check.payload(6,0)]" tcp-check expect rbinary "^520000000800000000 min-recv "9" \ error-status "L7STS" \ on-success "PostgreSQL server is ok" \ on-error "PostgreSQL unknown error" The log-format hexa string PGSQL_REQ depends on 2 preset variables, the packet length (check.plen) and the username (check.username).
This commit is contained in:
parent
fbcc77c6ba
commit
ce355074f1
@ -74,7 +74,9 @@ int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, s
|
||||
int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||
const char *file, int line);
|
||||
int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||
const char *file, int line);
|
||||
const char *file, int line);
|
||||
int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||
const char *file, int line);
|
||||
|
||||
#endif /* _PROTO_CHECKS_H */
|
||||
|
||||
|
@ -312,6 +312,7 @@ struct tcpcheck_rule {
|
||||
#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_PGSQL_CHK 0x00000010
|
||||
#define TCPCHK_RULES_REDIS_CHK 0x00000020
|
||||
#define TCPCHK_RULES_SMTP_CHK 0x00000030
|
||||
#define TCPCHK_RULES_SSL3_CHK 0x00000070
|
||||
|
@ -169,9 +169,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 */
|
||||
/* unused: 0x20000000 */
|
||||
/* unused 0x30000000 */
|
||||
/* unused: 0x10000000..0x30000000 */
|
||||
#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 */
|
||||
#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */
|
||||
|
@ -2407,63 +2407,8 @@ stats_error_parsing:
|
||||
goto out;
|
||||
}
|
||||
else if (!strcmp(args[1], "pgsql-check")) {
|
||||
/* use PostgreSQL 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_PGSQL_CHK;
|
||||
|
||||
if (*(args[2])) {
|
||||
int cur_arg = 2;
|
||||
|
||||
while (*(args[cur_arg])) {
|
||||
if (strcmp(args[cur_arg], "user") == 0) {
|
||||
char * packet;
|
||||
uint32_t packet_len;
|
||||
uint32_t pv;
|
||||
|
||||
/* suboption header - needs additional argument for it */
|
||||
if (*(args[cur_arg+1]) == 0) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
|
||||
file, linenum, args[0], args[1], args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* uint32_t + uint32_t + strlen("user")+1 + strlen(username)+1 + 1 */
|
||||
packet_len = 4 + 4 + 5 + strlen(args[cur_arg + 1])+1 +1;
|
||||
pv = htonl(0x30000); /* protocol version 3.0 */
|
||||
|
||||
packet = calloc(1, packet_len);
|
||||
|
||||
memcpy(packet + 4, &pv, 4);
|
||||
|
||||
/* copy "user" */
|
||||
memcpy(packet + 8, "user", 4);
|
||||
|
||||
/* copy username */
|
||||
memcpy(packet + 13, args[cur_arg+1], strlen(args[cur_arg+1]));
|
||||
|
||||
free(curproxy->check_req);
|
||||
curproxy->check_req = packet;
|
||||
curproxy->check_len = packet_len;
|
||||
|
||||
packet_len = htonl(packet_len);
|
||||
memcpy(packet, &packet_len, 4);
|
||||
cur_arg += 2;
|
||||
} else {
|
||||
/* unknown suboption - catchall */
|
||||
ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
|
||||
file, linenum, args[0], args[1]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
} /* end while loop */
|
||||
}
|
||||
if (alertif_too_many_args_idx(2, 1, file, linenum, args, &err_code))
|
||||
err_code |= proxy_parse_pgsql_check_opt(args, 0, curproxy, &defproxy, file, linenum);
|
||||
if (err_code & ERR_FATAL)
|
||||
goto out;
|
||||
}
|
||||
else if (!strcmp(args[1], "redis-check")) {
|
||||
|
179
src/checks.c
179
src/checks.c
@ -1156,27 +1156,6 @@ static void __event_srv_chk_r(struct conn_stream *cs)
|
||||
break;
|
||||
}
|
||||
|
||||
case PR_O2_PGSQL_CHK:
|
||||
if (!done && b_data(&check->bi) < 9)
|
||||
goto wait_more_data;
|
||||
|
||||
/* do not reset when closing, servers don't like this */
|
||||
if (conn_ctrl_ready(cs->conn))
|
||||
fdtab[cs->conn->handle.fd].linger_risk = 0;
|
||||
|
||||
if (b_head(&check->bi)[0] == 'R') {
|
||||
set_server_check_status(check, HCHK_STATUS_L7OKD, "PostgreSQL server is ok");
|
||||
}
|
||||
else {
|
||||
if ((b_head(&check->bi)[0] == 'E') && (b_head(&check->bi)[5]!=0) && (b_head(&check->bi)[6]!=0))
|
||||
desc = &b_head(&check->bi)[6];
|
||||
else
|
||||
desc = "PostgreSQL unknown error";
|
||||
|
||||
set_server_check_status(check, HCHK_STATUS_L7STS, desc);
|
||||
}
|
||||
break;
|
||||
|
||||
case PR_O2_MYSQL_CHK:
|
||||
if (!done && b_data(&check->bi) < 5)
|
||||
goto wait_more_data;
|
||||
@ -3512,7 +3491,7 @@ static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
|
||||
}
|
||||
|
||||
|
||||
static __maybe_unused struct tcpcheck_var *tcpcheck_var_create(const char *name)
|
||||
static struct tcpcheck_var *tcpcheck_var_create(const char *name)
|
||||
{
|
||||
struct tcpcheck_var *var = NULL;
|
||||
|
||||
@ -5400,6 +5379,162 @@ int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struc
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Parses the "option pgsql-check" proxy keyword */
|
||||
int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
|
||||
const char *file, int line)
|
||||
{
|
||||
static char pgsql_req[] = {
|
||||
"%[var(check.plen),htonl,hex]" /* The packet length*/
|
||||
"00030000" /* the version 3.0 */
|
||||
"7573657200" /* "user" key */
|
||||
"%[var(check.username),hex]00" /* the username */
|
||||
"00"
|
||||
};
|
||||
|
||||
struct tcpcheck_ruleset *rs = NULL;
|
||||
struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
|
||||
struct tcpcheck_rule *chk;
|
||||
struct tcpcheck_var *var = NULL;
|
||||
char *user = NULL, *errmsg = NULL;
|
||||
size_t packetlen = 0;
|
||||
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(2, 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;
|
||||
|
||||
cur_arg += 2;
|
||||
if (!*args[cur_arg] || !*args[cur_arg+1]) {
|
||||
ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
|
||||
file, line, args[0], args[1]);
|
||||
goto error;
|
||||
}
|
||||
if (strcmp(args[cur_arg], "user") == 0) {
|
||||
packetlen = 15 + strlen(args[cur_arg+1]);
|
||||
user = strdup(args[cur_arg+1]);
|
||||
|
||||
var = tcpcheck_var_create("check.username");
|
||||
if (user == NULL || var == NULL) {
|
||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||
goto error;
|
||||
}
|
||||
var->data.type = SMP_T_STR;
|
||||
var->data.u.str.area = user;
|
||||
var->data.u.str.data = strlen(user);
|
||||
LIST_INIT(&var->list);
|
||||
LIST_ADDQ(&rules->preset_vars, &var->list);
|
||||
user = NULL;
|
||||
var = NULL;
|
||||
|
||||
var = tcpcheck_var_create("check.plen");
|
||||
if (var == NULL) {
|
||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||
goto error;
|
||||
}
|
||||
var->data.type = SMP_T_SINT;
|
||||
var->data.u.sint = packetlen;
|
||||
LIST_INIT(&var->list);
|
||||
LIST_ADDQ(&rules->preset_vars, &var->list);
|
||||
var = NULL;
|
||||
}
|
||||
else {
|
||||
ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
|
||||
file, line, args[0], args[1]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rs = tcpcheck_ruleset_lookup("*pgsql-check");
|
||||
if (rs)
|
||||
goto ruleset_found;
|
||||
|
||||
rs = tcpcheck_ruleset_create("*pgsql-check");
|
||||
if (rs == NULL) {
|
||||
ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
|
||||
goto error;
|
||||
}
|
||||
|
||||
chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
|
||||
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_send((char *[]){"tcp-check", "send-binary", pgsql_req, "log-format", ""},
|
||||
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);
|
||||
|
||||
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
|
||||
"min-recv", "5",
|
||||
"error-status", "L7RSP",
|
||||
"on-error", "%[check.payload(6,0)]",
|
||||
""},
|
||||
1, curpx, &rs->rules, file, line, &errmsg);
|
||||
if (!chk) {
|
||||
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||
goto error;
|
||||
}
|
||||
chk->index = 2;
|
||||
LIST_ADDQ(&rs->rules, &chk->list);
|
||||
|
||||
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^520000000800000000",
|
||||
"min-recv", "9",
|
||||
"error-status", "L7STS",
|
||||
"on-success", "PostgreSQL server is ok",
|
||||
"on-error", "PostgreSQL unknown error",
|
||||
""},
|
||||
1, curpx, &rs->rules, file, line, &errmsg);
|
||||
if (!chk) {
|
||||
ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
|
||||
goto error;
|
||||
}
|
||||
chk->index = 3;
|
||||
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_PGSQL_CHK);
|
||||
|
||||
out:
|
||||
free(errmsg);
|
||||
return err_code;
|
||||
|
||||
error:
|
||||
free(user);
|
||||
free(var);
|
||||
free_tcpcheck_vars(&rules->preset_vars);
|
||||
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 },
|
||||
|
Loading…
Reference in New Issue
Block a user