MEDIUM: check/spoe: Use SPOP multiplexer to perform SPOP health-checks

The SPOP health-checks are now performed using the SPOP multiplexer. This
will be fixed later, but for now, it is considered as a L4 health-check and
no specific status code is reported. It means the corresponding vtest script
is marked as broken for now.

Functionnaly speaking, the same is performed. A connection is opened, a
HELLO frame is sent to the agent and we wait for the HELLO frame from the
agent in reply. But only L4OK, L4KO or L4TOUT will be reported.

The related issue is #2502.
This commit is contained in:
Christopher Faulet 2024-07-04 14:30:23 +02:00
parent 7e1bb7283b
commit 1bea73612a
4 changed files with 4 additions and 164 deletions

View File

@ -89,10 +89,6 @@ int init_srv_check(struct server *srv);
int init_srv_agent_check(struct server *srv);
int start_check_task(struct check *check, int mininter, int nbcheck, int srvpos);
/* Declared here, but the definitions are in flt_spoe.c */
int spoe_prepare_healthcheck_request(char **req, int *len);
int spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen);
int set_srv_agent_send(struct server *srv, const char *send);
/* set agent addr and appropriate flag */

View File

@ -2,6 +2,8 @@ varnishtest "Health-checks: SPOP health-check"
#REGTEST_TYPE=slow
feature ignore_unknown_macro
#REGTEST_TYPE=broken
# This scripts tests health-checks for SPOE agent, enabled using
# "option spop-check" line. A intermediate listener is used to validate
# the request because it is impossible with VTEST to read and match raw

View File

@ -970,80 +970,6 @@ spoe_handle_agentack_frame(struct appctx *appctx, char *frame, size_t size)
return (p - frame);
}
/* This function is used in cfgparse.c and declared in proto/checks.h. It
* prepare the request to send to agents during a healthcheck. It returns 0 on
* success and -1 if an error occurred. */
int
spoe_prepare_healthcheck_request(char **req, int *len)
{
struct appctx appctx;
struct spoe_appctx spoe_appctx;
char *frame, *end, buf[SPOP_MAX_FRAME_SIZE+4];
size_t sz;
int ret;
memset(&appctx, 0, sizeof(appctx));
memset(&spoe_appctx, 0, sizeof(spoe_appctx));
memset(buf, 0, sizeof(buf));
appctx.svcctx = &spoe_appctx;
SPOE_APPCTX(&appctx)->max_frame_size = SPOP_MAX_FRAME_SIZE;
frame = buf+4; /* Reserved the 4 first bytes for the frame size */
end = frame + SPOP_MAX_FRAME_SIZE;
ret = spoe_prepare_hahello_frame(&appctx, frame, SPOP_MAX_FRAME_SIZE);
if (ret <= 0)
return -1;
frame += ret;
/* Add "healthcheck" K/V item */
sz = SLEN(HEALTHCHECK_KEY);
if (spoe_encode_buffer(HEALTHCHECK_KEY, sz, &frame, end) == -1)
return -1;
*frame++ = (SPOP_DATA_T_BOOL | SPOP_DATA_FL_TRUE);
*len = frame - buf;
sz = htonl(*len - 4);
memcpy(buf, (char *)&sz, 4);
if ((*req = malloc(*len)) == NULL)
return -1;
memcpy(*req, buf, *len);
return 0;
}
/* This function is used in checks.c and declared in proto/checks.h. It decode
* the response received from an agent during a healthcheck. It returns 0 on
* success and -1 if an error occurred. */
int
spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen)
{
struct appctx appctx;
struct spoe_appctx spoe_appctx;
memset(&appctx, 0, sizeof(appctx));
memset(&spoe_appctx, 0, sizeof(spoe_appctx));
appctx.svcctx = &spoe_appctx;
SPOE_APPCTX(&appctx)->max_frame_size = SPOP_MAX_FRAME_SIZE;
if (*frame == SPOP_FRM_T_AGENT_DISCON) {
spoe_handle_agentdiscon_frame(&appctx, frame, size);
goto error;
}
if (spoe_handle_agenthello_frame(&appctx, frame, size) <= 0)
goto error;
return 0;
error:
if (SPOE_APPCTX(&appctx)->status_code >= SPOP_ERR_ENTRIES)
SPOE_APPCTX(&appctx)->status_code = SPOP_ERR_UNKNOWN;
strncpy(err, spoe_frm_err_reasons[SPOE_APPCTX(&appctx)->status_code], errlen);
return -1;
}
/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
* the frame can be ignored, 1 to retry later, and the frame length on
* success. */

View File

@ -759,55 +759,6 @@ enum tcpcheck_eval_ret tcpcheck_ldap_expect_bindrsp(struct check *check, struct
goto out;
}
/* Custom tcp-check expect function to parse and validate the SPOP hello agent
* frame. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
* to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
*/
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;
TRACE_ENTER(CHK_EV_TCPCHK_EXP, check);
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;
}
status = ((rule->expect.ok_status != HCHK_STATUS_UNKNOWN) ? rule->expect.ok_status : HCHK_STATUS_L7OKD);
set_server_check_status(check, status, "SPOA server is ok");
out:
free_trash_chunk(msg);
TRACE_LEAVE(CHK_EV_TCPCHK_EXP, check, 0, 0, (size_t[]){ret});
return ret;
error:
ret = TCPCHK_EVAL_STOP;
msg = alloc_trash_chunk();
if (msg)
tcpcheck_expect_onerror_message(msg, check, rule, 0, desc);
set_server_check_status(check, status, (msg ? b_head(msg) : NULL));
goto out;
wait_more_data:
TRACE_DEVEL("waiting for more data", CHK_EV_TCPCHK_EXP, check);
ret = TCPCHK_EVAL_WAIT;
goto out;
}
/* Custom tcp-check expect function to parse and validate the agent-check
* reply. Returns TCPCHK_EVAL_WAIT to wait for more data, TCPCHK_EVAL_CONTINUE
* to evaluate the next rule or TCPCHK_EVAL_STOP if an error occurred.
@ -1239,9 +1190,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && check->mux_proto)
mux_ops = check->mux_proto->mux;
else {
int mode = ((check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK
? PROTO_MODE_HTTP
: PROTO_MODE_TCP);
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck_rules->flags);
mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
}
@ -4842,10 +4791,7 @@ int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, co
{
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;
int err_code = 0;
if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
err_code |= ERR_WARN;
@ -4860,7 +4806,6 @@ int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, co
rules->list = NULL;
rules->flags = 0;
rs = find_tcpcheck_ruleset("*spop-check");
if (rs)
goto ruleset_found;
@ -4871,41 +4816,12 @@ int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, co
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_APPEND(&rs->rules, &chk->list);
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, 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_APPEND(&rs->rules, &chk->list);
ruleset_found:
rules->list = &rs->rules;
rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
rules->flags |= TCPCHK_RULES_SPOP_CHK;
out:
free(spop_req);
free(errmsg);
return err_code;
error: