MINOR: spoe/checks: Add support for SPOP health checks
A new "option spop-check" statement has been added to enable server health
checks based on SPOP HELLO handshake. SPOP is the protocol used by SPOE filters
to talk to servers.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index acd570d..7b05727 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -5118,6 +5118,34 @@
if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
goto out;
}
+ else if (!strcmp(args[1], "spop-check")) {
+ if (curproxy == &defproxy) {
+ Alert("parsing [%s:%d] : '%s %s' not allowed in 'defaults' section.\n",
+ file, linenum, args[0], args[1]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ if (curproxy->cap & PR_CAP_FE) {
+ Alert("parsing [%s:%d] : '%s %s' not allowed in 'frontend' and 'listen' sections.\n",
+ file, linenum, args[0], args[1]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ /* use SPOE request to check servers' health */
+ free(curproxy->check_req);
+ curproxy->check_req = NULL;
+ curproxy->options2 &= ~PR_O2_CHK_ANY;
+ curproxy->options2 |= PR_O2_SPOP_CHK;
+
+ if (prepare_spoe_healthcheck_request(&curproxy->check_req, &curproxy->check_len)) {
+ Alert("parsing [%s:%d] : failed to prepare SPOP healthcheck request.\n", file, linenum);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
+ goto out;
+ }
else if (!strcmp(args[1], "tcp-check")) {
/* use raw TCPCHK send/expect to check servers' health */
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL))
diff --git a/src/checks.c b/src/checks.c
index 65d0037..84a0f58 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1317,6 +1317,26 @@
}
break;
+ case PR_O2_SPOP_CHK: {
+ unsigned int framesz;
+ char err[HCHK_DESC_LEN];
+
+ if (!done && check->bi->i < 4)
+ goto wait_more_data;
+
+ memcpy(&framesz, check->bi->data, 4);
+ framesz = ntohl(framesz);
+
+ if (!done && check->bi->i < (4+framesz))
+ goto wait_more_data;
+
+ if (!handle_spoe_healthcheck_response(check->bi->data+4, framesz, err, HCHK_DESC_LEN-1))
+ set_server_check_status(check, HCHK_STATUS_L7OKD, "SPOA server is ok");
+ else
+ set_server_check_status(check, HCHK_STATUS_L7STS, err);
+ break;
+ }
+
default:
/* for other checks (eg: pure TCP), delegate to the main task */
break;
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 1ebdbda..12e589e 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -414,6 +414,7 @@
#define VERSION_KEY "version"
#define MAX_FRAME_SIZE_KEY "max-frame-size"
#define CAPABILITIES_KEY "capabilities"
+#define HEALTHCHECK_KEY "healthcheck"
#define STATUS_CODE_KEY "status-code"
#define MSG_KEY "message"
@@ -1075,6 +1076,70 @@
return idx;
}
+/* 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
+prepare_spoe_healthcheck_request(char **req, int *len)
+{
+ struct appctx a;
+ char *frame, buf[global.tune.bufsize];
+ unsigned int framesz;
+ int idx;
+
+ memset(&a, 0, sizeof(a));
+ memset(buf, 0, sizeof(buf));
+ APPCTX_SPOE(&a).max_frame_size = global.tune.bufsize;
+
+ frame = buf+4;
+ idx = prepare_spoe_hahello_frame(&a, frame, global.tune.bufsize-4);
+ if (idx <= 0)
+ return -1;
+ if (idx + SLEN(HEALTHCHECK_KEY) + 1 > global.tune.bufsize-4)
+ return -1;
+
+ /* "healthcheck" K/V item */
+ idx += encode_spoe_string(HEALTHCHECK_KEY, SLEN(HEALTHCHECK_KEY), frame+idx);
+ frame[idx++] = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
+
+ framesz = htonl(idx);
+ memcpy(buf, (char *)&framesz, 4);
+
+ if ((*req = malloc(idx+4)) == NULL)
+ return -1;
+ memcpy(*req, buf, idx+4);
+ *len = idx+4;
+ 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
+handle_spoe_healthcheck_response(char *frame, size_t size, char *err, int errlen)
+{
+ struct appctx a;
+ int r;
+
+ memset(&a, 0, sizeof(a));
+ APPCTX_SPOE(&a).max_frame_size = global.tune.bufsize;
+
+ if (handle_spoe_agentdiscon_frame(&a, frame, size) != 0)
+ goto error;
+ if ((r = handle_spoe_agenthello_frame(&a, frame, size)) <= 0) {
+ if (r == 0)
+ spoe_status_code = SPOE_FRM_ERR_INVALID;
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ if (spoe_status_code >= SPOE_FRM_ERRS)
+ spoe_status_code = SPOE_FRM_ERR_UNKNOWN;
+ strncpy(err, spoe_frm_err_reasons[spoe_status_code], errlen);
+ return -1;
+}
/********************************************************************
* Functions that manage the SPOE applet