mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-05-04 12:41:00 +02:00
[MEDIUM] add the "force-persist" statement to force persistence on down servers
This is used to force access to down servers for some requests. This is useful when validating that a change on a server correctly works before enabling the server again. (cherry picked from commit 4de9149f876cc0c63495b71a2c7a3aefc722c9c0)
This commit is contained in:
parent
32efff0c67
commit
59f50cce06
@ -1615,6 +1615,34 @@ errorloc303 <code> <url>
|
||||
See also : "errorfile", "errorloc", "errorloc302"
|
||||
|
||||
|
||||
force-persist { if | unless } <condition>
|
||||
Declare a condition to force persistence on down servers
|
||||
May be used in sections: defaults | frontend | listen | backend
|
||||
no | yes | yes | yes
|
||||
|
||||
By default, requests are not dispatched to down servers. It is possible to
|
||||
force this using "option persist", but it is unconditional and redispatches
|
||||
to a valid server if "option redispatch" is set. That leaves with very little
|
||||
possibilities to force some requests to reach a server which is artificially
|
||||
marked down for maintenance operations.
|
||||
|
||||
The "force-persist" statement allows one to declare various ACL-based
|
||||
conditions which, when met, will cause a request to ignore the down status of
|
||||
a server and still try to connect to it. That makes it possible to start a
|
||||
server, still replying an error to the health checks, and run a specially
|
||||
configured browser to test the service. Among the handy methods, one could
|
||||
use a specific source IP address, or a specific cookie. The cookie also has
|
||||
the advantage that it can easily be added/removed on the browser from a test
|
||||
page. Once the service is validated, it is then possible to open the service
|
||||
to the world by returning a valid response to health checks.
|
||||
|
||||
The forced persistence is enabled when an "if" condition is met, or unless an
|
||||
"unless" condition is met. The final redispatch is always disabled when this
|
||||
is used.
|
||||
|
||||
See also : "option redispatch", "persist", and section 7 about ACL usage.
|
||||
|
||||
|
||||
fullconn <conns>
|
||||
Specify at what backend load the servers will reach their maxconn
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
@ -2617,7 +2645,7 @@ no option persist
|
||||
If this option has been enabled in a "defaults" section, it can be disabled
|
||||
in a specific instance by prepending the "no" keyword before it.
|
||||
|
||||
See also : "option redispatch", "retries"
|
||||
See also : "option redispatch", "retries", "force-persist"
|
||||
|
||||
|
||||
option redispatch
|
||||
@ -2644,7 +2672,7 @@ no option redispatch
|
||||
If this option has been enabled in a "defaults" section, it can be disabled
|
||||
in a specific instance by prepending the "no" keyword before it.
|
||||
|
||||
See also : "redispatch", "retries"
|
||||
See also : "redispatch", "retries", "force-persist"
|
||||
|
||||
|
||||
option smtpchk
|
||||
|
||||
@ -160,6 +160,7 @@ struct proxy {
|
||||
struct list block_cond; /* early blocking conditions (chained) */
|
||||
struct list redirect_rules; /* content redirecting rules (chained) */
|
||||
struct list switching_rules; /* content switching rules (chained) */
|
||||
struct list force_persist_rules; /* 'force-persist' rules (chained) */
|
||||
struct { /* TCP request processing */
|
||||
unsigned int inspect_delay; /* inspection delay */
|
||||
struct list inspect_rules; /* inspection rules */
|
||||
@ -305,6 +306,11 @@ struct switching_rule {
|
||||
} be;
|
||||
};
|
||||
|
||||
struct force_persist_rule {
|
||||
struct list list; /* list linked to from the proxy */
|
||||
struct acl_cond *cond; /* acl condition to meet */
|
||||
};
|
||||
|
||||
struct redirect_rule {
|
||||
struct list list; /* list linked to from the proxy */
|
||||
struct acl_cond *cond; /* acl condition to meet */
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
#define SN_REDISP 0x00000100 /* set if this session was redispatched from one server to another */
|
||||
#define SN_CONN_TAR 0x00000200 /* set if this session is turning around before reconnecting */
|
||||
#define SN_REDIRECTABLE 0x00000400 /* set if this session is redirectable (GET or HEAD) */
|
||||
/* unused: 0x00000800 */
|
||||
#define SN_FORCE_PRST 0x00000800 /* force persistence here, even if server is down */
|
||||
|
||||
/* session termination conditions, bits values 0x1000 to 0x7000 (0-7 shift 12) */
|
||||
#define SN_ERR_NONE 0x00000000
|
||||
|
||||
@ -2019,7 +2019,8 @@ int srv_redispatch_connect(struct session *t)
|
||||
* would bring us on the same server again. Note that t->srv is set in
|
||||
* this case.
|
||||
*/
|
||||
if ((t->flags & SN_DIRECT) && (t->be->options & PR_O_REDISP)) {
|
||||
if (((t->flags & (SN_DIRECT|SN_FORCE_PRST)) == SN_DIRECT) &&
|
||||
(t->be->options & PR_O_REDISP)) {
|
||||
t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
|
||||
t->prev_srv = t->srv;
|
||||
goto redispatch;
|
||||
|
||||
@ -849,6 +849,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
||||
LIST_INIT(&curproxy->mon_fail_cond);
|
||||
LIST_INIT(&curproxy->switching_rules);
|
||||
LIST_INIT(&curproxy->tcp_req.inspect_rules);
|
||||
LIST_INIT(&curproxy->force_persist_rules);
|
||||
|
||||
/* Timeouts are defined as -1, so we cannot use the zeroed area
|
||||
* as a default value.
|
||||
@ -1728,6 +1729,56 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
||||
LIST_INIT(&rule->list);
|
||||
LIST_ADDQ(&curproxy->switching_rules, &rule->list);
|
||||
}
|
||||
else if (!strcmp(args[0], "force-persist")) {
|
||||
int pol = ACL_COND_NONE;
|
||||
struct acl_cond *cond;
|
||||
struct force_persist_rule *rule;
|
||||
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (warnifnotcap(curproxy, PR_CAP_FE|PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (!strcmp(args[1], "if"))
|
||||
pol = ACL_COND_IF;
|
||||
else if (!strcmp(args[1], "unless"))
|
||||
pol = ACL_COND_UNLESS;
|
||||
|
||||
if (pol == ACL_COND_NONE) {
|
||||
Alert("parsing [%s:%d] : '%s' requires either 'if' or 'unless' followed by a condition.\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((cond = parse_acl_cond((const char **)args + 2, &curproxy->acl, pol)) == NULL) {
|
||||
Alert("parsing [%s:%d] : error detected while parsing a 'force-persist' rule.\n",
|
||||
file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cond->line = linenum;
|
||||
if (cond->requires & ACL_USE_RTR_ANY) {
|
||||
struct acl *acl;
|
||||
const char *name;
|
||||
|
||||
acl = cond_find_require(cond, ACL_USE_RTR_ANY);
|
||||
name = acl ? acl->name : "(unknown)";
|
||||
Warning("parsing [%s:%d] : acl '%s' involves some response-only criteria which will be ignored.\n",
|
||||
file, linenum, name);
|
||||
err_code |= ERR_WARN;
|
||||
}
|
||||
|
||||
rule = (struct force_persist_rule *)calloc(1, sizeof(*rule));
|
||||
rule->cond = cond;
|
||||
LIST_INIT(&rule->list);
|
||||
LIST_ADDQ(&curproxy->force_persist_rules, &rule->list);
|
||||
}
|
||||
else if (!strcmp(args[0], "stats")) {
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
@ -65,7 +65,8 @@ static int redistribute_pending(struct server *s)
|
||||
|
||||
FOREACH_ITEM_SAFE(pc, pc_bck, &s->pendconns, pc_end, struct pendconn *, list) {
|
||||
struct session *sess = pc->sess;
|
||||
if (sess->be->options & PR_O_REDISP) {
|
||||
if ((sess->be->options & (PR_O_REDISP|PR_O_PERSIST)) == PR_O_REDISP &&
|
||||
!(sess->flags & SN_FORCE_PRST)) {
|
||||
/* The REDISP option was specified. We will ignore
|
||||
* cookie and force to balance or use the dispatcher.
|
||||
*/
|
||||
|
||||
@ -1540,6 +1540,7 @@ int http_process_request(struct session *s, struct buffer *req)
|
||||
struct http_txn *txn = &s->txn;
|
||||
struct http_msg *msg = &txn->req;
|
||||
struct proxy *cur_proxy;
|
||||
struct force_persist_rule *prst_rule;
|
||||
|
||||
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
||||
now_ms, __FUNCTION__,
|
||||
@ -2124,6 +2125,26 @@ int http_process_request(struct session *s, struct buffer *req)
|
||||
s->flags |= SN_BE_ASSIGNED;
|
||||
}
|
||||
|
||||
/* as soon as we know the backend, we must check if we have a matching forced
|
||||
* persistence rule, and report that in the session.
|
||||
*/
|
||||
list_for_each_entry(prst_rule, &s->be->force_persist_rules, list) {
|
||||
int ret = 1;
|
||||
|
||||
if (prst_rule->cond) {
|
||||
ret = acl_exec_cond(prst_rule->cond, s->be, s, &s->txn, ACL_DIR_REQ);
|
||||
ret = acl_pass(ret);
|
||||
if (prst_rule->cond->pol == ACL_COND_UNLESS)
|
||||
ret = !ret;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
/* no rule, or the rule matches */
|
||||
s->flags |= SN_FORCE_PRST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Right now, we know that we have processed the entire headers
|
||||
* and that unwanted requests have been filtered out. We can do
|
||||
@ -3428,7 +3449,9 @@ void manage_client_side_appsession(struct session *t, const char *buf) {
|
||||
struct server *srv = t->be->srv;
|
||||
while (srv) {
|
||||
if (strcmp(srv->id, asession->serverid) == 0) {
|
||||
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
|
||||
if ((srv->state & SRV_RUNNING) ||
|
||||
(t->be->options & PR_O_PERSIST) ||
|
||||
(t->flags & SN_FORCE_PRST)) {
|
||||
/* we found the server and it's usable */
|
||||
txn->flags &= ~TX_CK_MASK;
|
||||
txn->flags |= TX_CK_VALID;
|
||||
@ -3624,7 +3647,9 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
while (srv) {
|
||||
if (srv->cookie && (srv->cklen == delim - p3) &&
|
||||
!memcmp(p3, srv->cookie, delim - p3)) {
|
||||
if (srv->state & SRV_RUNNING || t->be->options & PR_O_PERSIST) {
|
||||
if ((srv->state & SRV_RUNNING) ||
|
||||
(t->be->options & PR_O_PERSIST) ||
|
||||
(t->flags & SN_FORCE_PRST)) {
|
||||
/* we found the server and it's usable */
|
||||
txn->flags &= ~TX_CK_MASK;
|
||||
txn->flags |= TX_CK_VALID;
|
||||
|
||||
@ -280,7 +280,8 @@ int sess_update_st_cer(struct session *s, struct stream_interface *si)
|
||||
* bit to ignore any persistence cookie. We won't count a retry nor a
|
||||
* redispatch yet, because this will depend on what server is selected.
|
||||
*/
|
||||
if (s->srv && s->conn_retries == 0 && s->be->options & PR_O_REDISP) {
|
||||
if (s->srv && s->conn_retries == 0 &&
|
||||
s->be->options & PR_O_REDISP && !(s->flags & SN_FORCE_PRST)) {
|
||||
if (may_dequeue_tasks(s->srv, s->be))
|
||||
process_srv_queue(s->srv);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user