MINOR: map/acl: add the "commit map/acl" CLI command

The command is used to atomically replace a map/acl with the pending
contents of the designated version. The new version must have been
allocated by "prepare map/acl" prior to this. At the moment it is not
possible to force the version when adding new entries, so this may only
be used to atomically clear an ACL/map.
This commit is contained in:
Willy Tarreau 2021-04-30 15:10:01 +02:00
parent 97218ce3a9
commit 7a562ca809
2 changed files with 95 additions and 0 deletions

View File

@ -1557,6 +1557,35 @@ clear table <table> [ data.<type> <operator> <value> ] | [ key <key> ]
$ echo "show table http_proxy" | socat stdio /tmp/sock1
>>> # table: http_proxy, type: ip, size:204800, used:1
commit acl @<ver> <acl>
Commit all changes made to version <ver> of ACL <acl>, and deletes all past
versions. <acl> is the #<id> or the <file> returned by "show acl". The
version number must be between "curr_ver"+1 and "next_ver" as reported in
"show acl". The contents to be committed to the ACL can be consulted with
"show acl @<ver> <acl>" if desired. The specified version number has normally
been created with the "prepare acl" command. The replacement is atomic. It
consists in atomically updating the current version to the specified version,
which will instantly cause all entries in other versions to become invisible,
and all entries in the new version to become visible. It is also possible to
use this command to perform an atomic removal of all visible entries of an
ACL by calling "prepare acl" first then committing without adding any
entries. This command cannot be used if the reference <acl> is a file also
used as a map. In this case, the "commit map" command must be used instead.
commit map @<ver> <map>
Commit all changes made to version <ver> of map <map>, and deletes all past
versions. <map> is the #<id> or the <file> returned by "show map". The
version number must be between "curr_ver"+1 and "next_ver" as reported in
"show map". The contents to be committed to the map can be consulted with
"show map @<ver> <map>" if desired. The specified version number has normally
been created with the "prepare map" command. The replacement is atomic. It
consists in atomically updating the current version to the specified version,
which will instantly cause all entries in other versions to become invisible,
and all entries in the new version to become visible. It is also possible to
use this command to perform an atomic removal of all visible entries of an
map by calling "prepare map" first then committing without adding any
entries.
commit ssl cert <filename>
Commit a temporary SSL certificate update transaction.

View File

@ -1040,17 +1040,83 @@ static int cli_parse_clear_map(char **args, char *payload, struct appctx *appctx
return 1;
}
/* note: sets appctx->ctx.cli.i0 and appctx->ctx.cli.i1 to the oldest and
* latest generations to clear, respectively, and will call the clear_map
* handler.
*/
static int cli_parse_commit_map(char **args, char *payload, struct appctx *appctx, void *private)
{
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
const char *gen = NULL;
uint genid;
uint ret;
/* Set ACL or MAP flags. */
if (args[1][0] == 'm')
appctx->ctx.map.display_flags = PAT_REF_MAP;
else
appctx->ctx.map.display_flags = PAT_REF_ACL;
if (*args[2] != '@')
return cli_err(appctx, "Missing version number.\n");
/* The generation number is mandatory for a commit. The range
* of generations that get trashed by a commit starts from the
* opposite of the current one and ends at the previous one.
*/
gen = args[2] + 1;
genid = str2uic(gen);
appctx->ctx.cli.i1 = genid - 1;
appctx->ctx.cli.i0 = appctx->ctx.cli.i1 - ((~0U) >> 1);
/* no parameter */
if (!*args[3]) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
return cli_err(appctx, "Missing map identifier.\n");
else
return cli_err(appctx, "Missing ACL identifier.\n");
}
/* lookup into the refs and check the map flag */
appctx->ctx.map.ref = pat_ref_lookup_ref(args[3]);
if (!appctx->ctx.map.ref ||
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
return cli_err(appctx, "Unknown map identifier. Please use #<id> or <file>.\n");
else
return cli_err(appctx, "Unknown ACL identifier. Please use #<id> or <file>.\n");
}
HA_SPIN_LOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
if (genid - (appctx->ctx.map.ref->curr_gen + 1) <
appctx->ctx.map.ref->next_gen - appctx->ctx.map.ref->curr_gen)
ret = pat_ref_commit(appctx->ctx.map.ref, genid);
else
ret = 1;
HA_SPIN_UNLOCK(PATREF_LOCK, &appctx->ctx.map.ref->lock);
if (ret != 0)
return cli_err(appctx, "Version number out of range.\n");
/* delegate the clearing to the I/O handler which can yield */
return 0;
}
return 1;
}
/* register cli keywords */
static struct cli_kw_list cli_kws = {{ },{
{ { "add", "acl", NULL }, "add acl : add acl entry", cli_parse_add_map, NULL },
{ { "clear", "acl", NULL }, "clear acl [@ver] <id> : clear the content of this acl", cli_parse_clear_map, cli_io_handler_clear_map, NULL },
{ { "commit","acl", NULL }, "commit acl @<ver> <id> : commit the ACL at this version", cli_parse_commit_map, cli_io_handler_clear_map, NULL },
{ { "del", "acl", NULL }, "del acl : delete acl entry", cli_parse_del_map, NULL },
{ { "get", "acl", NULL }, "get acl : report the patterns matching a sample for an ACL", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
{ { "prepare","acl",NULL }, "prepare acl <id>: prepare a new version for atomic ACL replacement", cli_parse_prepare_map, NULL },
{ { "show", "acl", NULL }, "show acl [@ver] [id] : report available acls or dump an acl's contents", cli_parse_show_map, NULL },
{ { "add", "map", NULL }, "add map : add map entry", cli_parse_add_map, NULL },
{ { "clear", "map", NULL }, "clear map [@ver] <id> : clear the content of this map", cli_parse_clear_map, cli_io_handler_clear_map, NULL },
{ { "commit","map", NULL }, "commit map @<ver> <id> : commit the map at this version", cli_parse_commit_map, cli_io_handler_clear_map, NULL },
{ { "del", "map", NULL }, "del map : delete map entry", cli_parse_del_map, NULL },
{ { "get", "map", NULL }, "get map : report the keys and values matching a sample for a map", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
{ { "prepare","map",NULL }, "prepare map <id>: prepare a new version for atomic map replacement", cli_parse_prepare_map, NULL },