MEDIUM: checks: Add status-code sample expression on tcp-check expect rules

This option defines a sample expression, evaluated as an integer, to set the
status code (check->code) if a tcp-check healthcheck ends on the corresponding
expect rule.
This commit is contained in:
Christopher Faulet 2020-04-01 20:52:31 +02:00
parent be52b4de66
commit 98cc57cf5c
3 changed files with 68 additions and 7 deletions

View File

@ -9873,7 +9873,7 @@ tcp-check connect [params*]
tcp-check expect [min-recv <int>] [error-status <st>] [tout-status <st>] tcp-check expect [min-recv <int>] [error-status <st>] [tout-status <st>]
[on-success <fmt>] [on-error <fmt>] [on-success <fmt>] [on-error <fmt>] [status-code <expr>]
[!] <match> <pattern> [!] <match> <pattern>
Specify data to be collected and analyzed during a generic health check Specify data to be collected and analyzed during a generic health check
May be used in sections: defaults | frontend | listen | backend May be used in sections: defaults | frontend | listen | backend
@ -9922,6 +9922,11 @@ tcp-check expect [min-recv <int>] [error-status <st>] [tout-status <st>]
occurred during the expect rule evaluation. <fmt> is a occurred during the expect rule evaluation. <fmt> is a
log-format string. log-format string.
status-code <expr> is optional and can be used to set the check status code
reported in logs, on success or on error. <expr> is a
standard HAProxy expression formed by a sample-fetch
followed by some converters.
<pattern> is the pattern to look for. It may be a string or a regular <pattern> is the pattern to look for. It may be a string or a regular
expression. If the pattern contains spaces, they must be escaped expression. If the pattern contains spaces, they must be escaped
with the usual backslash ('\'). with the usual backslash ('\').

View File

@ -268,6 +268,7 @@ struct tcpcheck_expect {
struct list onsuccess_fmt; /* log-format string to use as comment on success (if last rule) */ struct list onsuccess_fmt; /* log-format string to use as comment on success (if last rule) */
enum healthcheck_status err_status; /* The healthcheck status to use on error (default: L7RSP) */ enum healthcheck_status err_status; /* The healthcheck status to use on error (default: L7RSP) */
enum healthcheck_status tout_status; /* The healthcheck status to use on timeout (default: L7TOUT) */ enum healthcheck_status tout_status; /* The healthcheck status to use on timeout (default: L7TOUT) */
struct sample_expr *status_expr; /* sample expr to determine the check status code */
}; };
struct tcpcheck_action_kw { struct tcpcheck_action_kw {

View File

@ -3187,6 +3187,16 @@ static enum tcpcheck_eval_ret tcpcheck_eval_expect(struct check *check, struct t
chunk_appendf(msg, " comment: '%s'", rule->comment); chunk_appendf(msg, " comment: '%s'", rule->comment);
} }
if (expect->status_expr) {
struct sample *smp;
smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
SMP_OPT_DIR_RES | SMP_OPT_FINAL,
expect->status_expr, SMP_T_SINT);
if (smp)
check->code = smp->data.u.sint;
}
no_desc: no_desc:
set_server_check_status(check, expect->err_status, (msg ? b_head(msg) : NULL)); set_server_check_status(check, expect->err_status, (msg ? b_head(msg) : NULL));
ret = TCPCHK_EVAL_STOP; ret = TCPCHK_EVAL_STOP;
@ -3386,12 +3396,23 @@ static int tcpcheck_main(struct check *check)
} }
/* All rules was evaluated */ /* All rules was evaluated */
if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT && if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
!LIST_ISEMPTY(&check->current_step->expect.onsuccess_fmt)) { if (!LIST_ISEMPTY(&check->current_step->expect.onsuccess_fmt)) {
msg = alloc_trash_chunk(); msg = alloc_trash_chunk();
if (msg) if (msg)
msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg), msg->data += sess_build_logline(check->sess, NULL, b_tail(msg), b_room(msg),
&check->current_step->expect.onsuccess_fmt); &check->current_step->expect.onsuccess_fmt);
}
if (check->current_step->expect.status_expr) {
struct sample *smp;
smp = sample_fetch_as_type(check->proxy, check->sess, NULL,
SMP_OPT_DIR_RES | SMP_OPT_FINAL,
check->current_step->expect.status_expr, SMP_T_SINT);
if (smp)
check->code = smp->data.u.sint;
}
} }
set_server_check_status(check, HCHK_STATUS_L7OKD, (msg ? b_head(msg) : "(tcp-check)")); set_server_check_status(check, HCHK_STATUS_L7OKD, (msg ? b_head(msg) : "(tcp-check)"));
@ -3492,6 +3513,7 @@ static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
free(lf->arg); free(lf->arg);
free(lf); free(lf);
} }
release_sample_expr(rule->expect.status_expr);
switch (rule->expect.type) { switch (rule->expect.type) {
case TCPCHK_EXPECT_STRING: case TCPCHK_EXPECT_STRING:
case TCPCHK_EXPECT_BINARY: case TCPCHK_EXPECT_BINARY:
@ -4556,6 +4578,7 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
const char *file, int line, char **errmsg) const char *file, int line, char **errmsg)
{ {
struct tcpcheck_rule *prev_check, *chk = NULL; struct tcpcheck_rule *prev_check, *chk = NULL;
struct sample_expr *status_expr = NULL;
char *str, *on_success_msg, *on_error_msg, *comment, *pattern; char *str, *on_success_msg, *on_error_msg, *comment, *pattern;
enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF; enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
enum healthcheck_status err_st = HCHK_STATUS_L7RSP; enum healthcheck_status err_st = HCHK_STATUS_L7RSP;
@ -4693,6 +4716,36 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
} }
cur_arg++; cur_arg++;
} }
else if (strcmp(args[cur_arg], "status-code") == 0) {
int idx = 0;
if (in_pattern) {
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
goto error;
}
if (!*(args[cur_arg+1])) {
memprintf(errmsg, "'%s' expects an expression as argument", args[cur_arg]);
goto error;
}
cur_arg++;
release_sample_expr(status_expr);
px->conf.args.ctx = ARGC_SRV;
status_expr = sample_parse_expr((char *[]){args[cur_arg], NULL}, &idx,
file, line, errmsg, &px->conf.args, NULL);
if (!status_expr) {
memprintf(errmsg, "error detected while parsing status-code expression : %s", *errmsg);
goto error;
}
if (!(status_expr->fetch->val & SMP_VAL_BE_CHK_RUL)) {
memprintf(errmsg, "error detected while parsing status-code expression : "
" fetch method '%s' extracts information from '%s', "
"none of which is available here.\n",
args[cur_arg], sample_src_names(status_expr->fetch->use));
goto error;
}
px->http_needed |= !!(status_expr->fetch->use & SMP_USE_HTTP_ANY);
}
else if (strcmp(args[cur_arg], "tout-status") == 0) { else if (strcmp(args[cur_arg], "tout-status") == 0) {
if (in_pattern) { if (in_pattern) {
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]); memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
@ -4758,6 +4811,7 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
chk->expect.with_capture = with_capture; chk->expect.with_capture = with_capture;
chk->expect.err_status = err_st; chk->expect.err_status = err_st;
chk->expect.tout_status = tout_st; chk->expect.tout_status = tout_st;
chk->expect.status_expr = status_expr; status_expr = NULL;
if (on_success_msg) { if (on_success_msg) {
px->conf.args.ctx = ARGC_SRV; px->conf.args.ctx = ARGC_SRV;
@ -4825,6 +4879,7 @@ static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, str
free(comment); free(comment);
free(on_success_msg); free(on_success_msg);
free(on_error_msg); free(on_error_msg);
release_sample_expr(status_expr);
return NULL; return NULL;
} }