MEDIUM: cfgparse: allow a proxy to designate the defaults section to use

Now it becomes possible to specify "from foo" on a frontend/listen/backend
or even on a "defaults" line, to mention that defaults section "foo" needs
to be used to preset the proxy's settings.

When not set, the last section remains used. In case the designated name
is found at multiple places, it is rejected and an error indicates two
occurrences of the same name. Similarly, if the section name is found,
its name must only use valid characters. This allows multiple named
defaults section to continue to coexist without the risk that they will
cause trouble by accident.

When it comes to "defaults" relying on another defaults, what happens is
just that a new defaults section is created from the designated one. This
will make it possible for example to reuse some settings such as log-format
like below:

    defaults tcp-clear
        log stdout local0 info
        log-format "%ci:%cp/%b/%si:%sp %ST %ts %U/%B %{+Q}r"

    defaults tcp-ssl
        log stdout local0 info
        log-format "%ci:%cp/%b/%si:%sp %ST %ts %U/%B %{+Q}r ssl=%sslv"

    defaults http-clear from tcp-clear
        mode http

    defaults http-ssl from tcp-ssl
        mode http

    frontend fe1 from http-clear
        bind :8001

    frontend fe2 from http-ssl
        bind :8002

A small corner case remains in the error detection, if a second defaults
section appears with the same name after the point where it was used, and
nobody references it, the duplicate will not be detected. This could be
addressed by performing the syntactic checks in check_config_validity(),
and by postponing the freeing of the defaults, after tagging a defaults
section as explicitly looked up by another section. This doesn't seem
that important at the moment though.
This commit is contained in:
Willy Tarreau 2021-02-12 14:58:08 +01:00
parent e90904d5a9
commit 7c0b4d861e
2 changed files with 69 additions and 11 deletions

View File

@ -3119,15 +3119,10 @@ timeout client <timeout>
----------
Proxy configuration can be located in a set of sections :
- defaults [<name>]
- frontend <name>
- backend <name>
- listen <name>
A "defaults" section sets default parameters for all other sections following
its declaration. Those default parameters are reset by the next "defaults"
section. See below for the list of parameters which can be set in a "defaults"
section. The name is optional but its use is encouraged for better readability.
- defaults [<name>] [ from <defaults_name> ]
- frontend <name> [ from <defaults_name> ]
- backend <name> [ from <defaults_name> ]
- listen <name> [ from <defaults_name> ]
A "frontend" section describes a set of listening sockets accepting client
connections.
@ -3138,6 +3133,29 @@ to forward incoming connections.
A "listen" section defines a complete proxy with its frontend and backend
parts combined in one section. It is generally useful for TCP-only traffic.
A "defaults" section resets all settings to the documented ones and presets new
ones for use by subsequent sections. All of "frontend", "backend" and "listen"
sections always take their initial settings from a defaults section, by default
the latest one that appears before the newly created section. It is possible to
explicitly designate a specific "defaults" section to load the initial settings
from by indicating its name on the section line after the optional keyword
"from". While "defaults" section do not impose a name, this use is encouraged
for better readability. It is also the only way to designate a specific section
to use instead of the default previous one. Since "defaults" section names are
optional, by default a very permissive check is applied on their name and these
are even permitted to overlap. However if a "defaults" section is referenced by
any other section, its name must comply with the syntax imposed on all proxy
names, and this name must be unique among the defaults sections. Please note
that regardless of what is currently permitted, it is recommended to avoid
duplicate section names in general and to respect the same syntax as for proxy
names. This rule might be enforced in a future version.
Note that it is even possible for a defaults section to take its initial
settings from another one, and as such, inherit settings across multiple levels
of defaults sections. This can be convenient to establish certain configuration
profiles to carry groups of default settings (e.g. TCP vs HTTP or short vs long
timeouts) but can quickly become confusing to follow.
All proxy names must be formed from upper and lower case letters, digits,
'-' (dash), '_' (underscore) , '.' (dot) and ':' (colon). ACL names are
case-sensitive, which means that "www" and "WWW" are two different proxies.

View File

@ -237,7 +237,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
err_code |= ERR_ALERT | ERR_FATAL;
}
if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
if ((*args[2] && (!*args[3] || strcmp(args[2], "from") != 0)) ||
alertif_too_many_args(3, file, linenum, args, &err_code)) {
if (rc & PR_CAP_FE)
ha_alert("parsing [%s:%d] : please use the 'bind' keyword for listening addresses.\n", file, linenum);
goto out;
@ -245,7 +246,46 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
}
if (rc & PR_CAP_LISTEN) { /* new proxy or defaults section */
curproxy = alloc_new_proxy(args[1], rc, file, linenum, curr_defproxy, &errmsg);
const char *name = args[1];
int arg = 2;
if (rc & PR_CAP_DEF && strcmp(args[1], "from") == 0 && *args[2] && !*args[3]) {
// also support "defaults from blah" (no name then)
arg = 1;
name = "";
}
/* only regular proxies inherit from the previous defaults section */
if (!(rc & PR_CAP_DEF))
curr_defproxy = last_defproxy;
if (strcmp(args[arg], "from") == 0) {
curr_defproxy = proxy_find_by_name(args[arg+1], PR_CAP_DEF, 0);
if (!curr_defproxy) {
ha_alert("parsing [%s:%d] : defaults section '%s' not found for %s '%s'.\n", file, linenum, args[arg+1], proxy_cap_str(rc), name);
err_code |= ERR_ALERT | ERR_ABORT;
goto out;
}
if (ebpt_next_dup(&curr_defproxy->conf.by_name)) {
struct proxy *px2 = container_of(ebpt_next_dup(&curr_defproxy->conf.by_name), struct proxy, conf.by_name);
ha_alert("parsing [%s:%d] : ambiguous defaults section name '%s' referenced by %s '%s' exists at least at %s:%d and %s:%d.\n",
file, linenum, args[arg+1], proxy_cap_str(rc), name,
curr_defproxy->conf.file, curr_defproxy->conf.line, px2->conf.file, px2->conf.line);
err_code |= ERR_ALERT | ERR_FATAL;
}
err = invalid_char(args[arg+1]);
if (err) {
ha_alert("parsing [%s:%d] : character '%c' is not permitted in defaults section name '%s' when designated by its name (section found at %s:%d).\n",
file, linenum, *err, args[arg+1], curr_defproxy->conf.file, curr_defproxy->conf.line);
err_code |= ERR_ALERT | ERR_FATAL;
}
}
curproxy = alloc_new_proxy(name, rc, file, linenum, curr_defproxy, &errmsg);
if (!curproxy) {
ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg);
err_code |= ERR_ALERT | ERR_ABORT;