mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-01-19 09:01:05 +01:00
MEDIUM: proxy: force traffic on unpublished/disabled backends
A recent patch has introduced a new state for proxies : unpublished backends. Such backends won't be eligilible for traffic, thus use_backend/default_backend rules which target them won't match and content switching rules processing will continue. This patch defines a new frontend keywords 'force-be-switch'. This keyword allows to ignore unpublished or disabled state. Thus, use_backend/default_backend will match even if the target backend is unpublished or disabled. This is useful to be able to test a backend instance before exposing it outside. This new keyword is converted into a persist rule of new type PERSIST_TYPE_BE_SWITCH, stored in persist_rules list proxy member. This is the only persist rule applicable to frontend side. Prior to this commit, pure frontend proxies persist_rules list were always empty. This new features requires adjustment in process_switching_rules(). Now, when a use_backend/default_backend rule matches with an non eligible backend, frontend persist_rules are inspected to detect if a force-be-switch is present so that the backend may be selected.
This commit is contained in:
parent
16f035d555
commit
6870551a57
@ -5820,6 +5820,7 @@ errorloc302 X X X X
|
||||
errorloc303 X X X X
|
||||
error-log-format X X X -
|
||||
force-persist - - X X
|
||||
force-be-switch - X X -
|
||||
filter - X X X
|
||||
fullconn X - X X
|
||||
guid - X X X
|
||||
@ -7149,7 +7150,11 @@ disabled
|
||||
is possible to disable many instances at once by adding the "disabled"
|
||||
keyword in a "defaults" section.
|
||||
|
||||
See also : "enabled"
|
||||
By default, a disabled backend cannot be selected for content-switching.
|
||||
However, a portion of the traffic can ignore this when "force-be-switch" is
|
||||
used.
|
||||
|
||||
See also : "enabled", "force-be-switch"
|
||||
|
||||
|
||||
dispatch <address>:<port> (deprecated)
|
||||
@ -7559,6 +7564,19 @@ force-persist { if | unless } <condition>
|
||||
and section 7 about ACL usage.
|
||||
|
||||
|
||||
force-be-switch { if | unless } <condition>
|
||||
Allow content switching to select a backend instance even if it is disabled
|
||||
or unpublished. This rule can be used by admins to test traffic to services
|
||||
prior to expose them to the outside world.
|
||||
|
||||
May be used in the following contexts: tcp, http
|
||||
|
||||
May be used in sections: defaults | frontend | listen | backend
|
||||
no | yes | yes | no
|
||||
|
||||
See also : "disabled"
|
||||
|
||||
|
||||
filter <name> [param*]
|
||||
Add the filter <name> in the filter list attached to the proxy.
|
||||
|
||||
|
||||
@ -184,6 +184,7 @@ enum {
|
||||
PERSIST_TYPE_NONE = 0, /* no persistence */
|
||||
PERSIST_TYPE_FORCE, /* force-persist */
|
||||
PERSIST_TYPE_IGNORE, /* ignore-persist */
|
||||
PERSIST_TYPE_BE_SWITCH, /* force-be-switch */
|
||||
};
|
||||
|
||||
/* final results for http-request rules */
|
||||
|
||||
@ -33,12 +33,14 @@ haproxy h1 -conf {
|
||||
|
||||
frontend fe
|
||||
bind "fd@${fe1S}"
|
||||
|
||||
use_backend %[req.hdr("x-target")] if { req.hdr("x-dyn") "1" }
|
||||
use_backend be if { req.hdr("x-target") "be" }
|
||||
|
||||
frontend fe_default
|
||||
bind "fd@${fe2S}"
|
||||
|
||||
force-be-switch if { req.hdr("x-force") "1" }
|
||||
use_backend %[req.hdr("x-target")] if { req.hdr("x-dyn") "1" }
|
||||
use_backend be_disabled if { req.hdr("x-target") "be_disabled" }
|
||||
use_backend be
|
||||
@ -136,6 +138,18 @@ client c4 -connect ${h1_fe2S_sock} {
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-be == "be2"
|
||||
|
||||
# Static rule matching on unpublished backend with force-be-switch
|
||||
txreq -hdr "x-force: 1"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-be == "be"
|
||||
|
||||
# Dynamic rule matching on unpublished backend with force-be-switch
|
||||
txreq -hdr "x-dyn: 1" -hdr "x-target: be" -hdr "x-force: 1"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-be == "be"
|
||||
} -run
|
||||
|
||||
haproxy h1 -cli {
|
||||
|
||||
51
src/proxy.c
51
src/proxy.c
@ -1160,6 +1160,56 @@ static int proxy_parse_tcpka_intvl(char **args, int section, struct proxy *proxy
|
||||
}
|
||||
#endif
|
||||
|
||||
static int proxy_parse_force_be_switch(char **args, int section_type, struct proxy *curpx,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
{
|
||||
struct acl_cond *cond = NULL;
|
||||
struct persist_rule *rule;
|
||||
|
||||
if (curpx->cap & PR_CAP_DEF) {
|
||||
memprintf(err, "'%s' not allowed in 'defaults' section.", args[0]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(curpx->cap & PR_CAP_FE)) {
|
||||
memprintf(err, "'%s' only available in frontend or listen section.", args[0]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(args[1], "if") != 0 && strcmp(args[1], "unless") != 0) {
|
||||
memprintf(err, "'%s' requires either 'if' or 'unless' followed by a condition.", args[0]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args + 1, err))) {
|
||||
memprintf(err, "'%s' : %s.", args[0], *err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (warnif_cond_conflicts(cond, SMP_VAL_FE_REQ_CNT, err)) {
|
||||
memprintf(err, "'%s' : %s.", args[0], *err);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rule = calloc(1, sizeof(*rule));
|
||||
if (!rule) {
|
||||
memprintf(err, "'%s' : out of memory.", args[0]);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rule->cond = cond;
|
||||
rule->type = PERSIST_TYPE_BE_SWITCH;
|
||||
LIST_INIT(&rule->list);
|
||||
LIST_APPEND(&curpx->persist_rules, &rule->list);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free_acl_cond(cond);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int proxy_parse_guid(char **args, int section_type, struct proxy *curpx,
|
||||
const struct proxy *defpx, const char *file, int line,
|
||||
char **err)
|
||||
@ -2869,6 +2919,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_LISTEN, "clitcpka-intvl", proxy_parse_tcpka_intvl },
|
||||
{ CFG_LISTEN, "srvtcpka-intvl", proxy_parse_tcpka_intvl },
|
||||
#endif
|
||||
{ CFG_LISTEN, "force-be-switch", proxy_parse_force_be_switch },
|
||||
{ CFG_LISTEN, "guid", proxy_parse_guid },
|
||||
{ 0, NULL, NULL },
|
||||
}};
|
||||
|
||||
47
src/stream.c
47
src/stream.c
@ -1117,6 +1117,41 @@ enum act_return process_use_service(struct act_rule *rule, struct proxy *px,
|
||||
return ACT_RET_STOP;
|
||||
}
|
||||
|
||||
/* Parses persist-rules attached to <fe> frontend and report the first macthing
|
||||
* entry, using <sess> session and <s> stream as sample source.
|
||||
*
|
||||
* As this function is called several times in the same stream context,
|
||||
* <persist> will act as a caching value to avoid reprocessing of a similar
|
||||
* ruleset. It must be set to a negative value for the first invokation.
|
||||
*
|
||||
* Returns 1 if a rule matches, else 0.
|
||||
*/
|
||||
static int lookup_fe_persist_rules(struct proxy *fe, struct session *sess,
|
||||
struct stream *s, int *persist)
|
||||
{
|
||||
struct persist_rule *prst_rule;
|
||||
|
||||
if (*persist >= 0) {
|
||||
/* Rules already processed, use previous computed result. */
|
||||
return *persist;
|
||||
}
|
||||
|
||||
list_for_each_entry(prst_rule, &fe->persist_rules, list) {
|
||||
if (!acl_match_cond(prst_rule->cond, fe, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL))
|
||||
continue;
|
||||
|
||||
/* force/ignore-persist match */
|
||||
if (prst_rule->type == PERSIST_TYPE_BE_SWITCH) {
|
||||
*persist = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*persist < 0)
|
||||
*persist = 0;
|
||||
return *persist;
|
||||
}
|
||||
|
||||
/* This stream analyser checks the switching rules and changes the backend
|
||||
* if appropriate. The default_backend rule is also considered, then the
|
||||
* target backend's forced persistence rules are also evaluated last if any.
|
||||
@ -1130,6 +1165,7 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an
|
||||
struct session *sess = s->sess;
|
||||
struct proxy *fe = sess->fe;
|
||||
struct proxy *backend = NULL;
|
||||
int fe_persist = -1;
|
||||
|
||||
req->analysers &= ~an_bit;
|
||||
req->analyse_exp = TICK_ETERNITY;
|
||||
@ -1160,7 +1196,8 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an
|
||||
}
|
||||
|
||||
/* If backend is ineligible, continue rules processing. */
|
||||
if (backend && !be_is_eligible(backend)) {
|
||||
if (backend && !be_is_eligible(backend) &&
|
||||
!lookup_fe_persist_rules(fe, sess, s, &fe_persist)) {
|
||||
backend = NULL;
|
||||
continue;
|
||||
}
|
||||
@ -1185,10 +1222,14 @@ static int process_switching_rules(struct stream *s, struct channel *req, int an
|
||||
}
|
||||
|
||||
/* Use default backend if possible or stay on the current proxy. */
|
||||
if (fe->defbe.be && be_is_eligible(fe->defbe.be))
|
||||
if (fe->defbe.be &&
|
||||
(be_is_eligible(fe->defbe.be) ||
|
||||
lookup_fe_persist_rules(fe, sess, s, &fe_persist))) {
|
||||
backend = fe->defbe.be;
|
||||
else
|
||||
}
|
||||
else {
|
||||
backend = s->be;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stream_set_backend(s, backend))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user