From 98c8c5e16e672da8f19184df485262a1a4a5b116 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 18 Feb 2026 14:00:08 +0100 Subject: [PATCH] MINOR: cli: implement wait on be-removable Implement be-removable argument to CLI wait. This is implemented via be_check_for_deletion() invokation, also used by "del backend" handler. The objective is to test whether a backend instance can be removed. If this is not the case, the command may returns immediately if the target proxy is incompatible with dynamic removal or if a user action is required. Else, the command will wait until the temporary restriction is lifted. --- doc/management.txt | 14 +++++++++++++- include/haproxy/cli-t.h | 1 + src/cli.c | 20 +++++++++++++++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/doc/management.txt b/doc/management.txt index 82f89305b..9cae22aae 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -2129,7 +2129,8 @@ del backend This operation is only possible for TCP or HTTP proxies. To succeed, the backend instance must have been first unpublished. Also, all of its servers - must first be removed (via "del server" CLI). + must first be removed (via "del server" CLI). Finally, no stream must still + be attached to the backend instance. There is additional restrictions which prevent backend removal. First, a backend cannot be removed if it is explicitely referenced by config elements, @@ -2139,6 +2140,10 @@ del backend cannot be removed if there is a stick-table declared in it. Finally, it is impossible for now to remove a backend if QUIC servers were present in it. + It can be useful to use "wait be-removable" prior to this command to check + for the aformentioned requisites. This also provides a methode to wait for + the final closure of the streams attached to the target backend. + This command is restricted and can only be issued on sockets configured for level "admin". Moreover, this feature is still considered in development so it also requires experimental mode (see "experimental-mode on"). @@ -4545,6 +4550,13 @@ wait { -h | } [ [...]] specified condition to be satisfied, to unrecoverably fail, or to remain unsatisfied for the whole duration. The supported conditions are: + - be-removable : this will wait for the specified proxy backend to be + removable by the "del backend" command. Some conditions will never be + accepted (e.g. backend not yet unpublished or with servers in it) and will + cause the report of a specific error message indicating what condition is + not met. If everything is OK before the delay, a success is returned and + the operation is terminated. + - srv-removable / : this will wait for the specified server to be removable by the "del server" command, i.e. be in maintenance and no longer have any connection on it (neither active or idle). Some conditions diff --git a/include/haproxy/cli-t.h b/include/haproxy/cli-t.h index a581f2a94..3332b6da6 100644 --- a/include/haproxy/cli-t.h +++ b/include/haproxy/cli-t.h @@ -100,6 +100,7 @@ enum cli_wait_err { enum cli_wait_cond { CLI_WAIT_COND_NONE, // no condition to wait on CLI_WAIT_COND_SRV_UNUSED,// wait for server to become unused + CLI_WAIT_COND_BE_UNUSED, // wait for backend to become unused }; struct cli_wait_ctx { diff --git a/src/cli.c b/src/cli.c index ab5a008f3..312e2a2cb 100644 --- a/src/cli.c +++ b/src/cli.c @@ -2120,6 +2120,14 @@ static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, voi ctx->args[1] = ist0(sv_name); ctx->cond = CLI_WAIT_COND_SRV_UNUSED; } + else if (strcmp(args[2], "be-removable") == 0) { + if (!*args[3]) + return cli_err(appctx, "Missing backend name.\n"); + ctx->args[0] = strdup(args[3]); + if (!ctx->args[0]) + return cli_err(appctx, "Out of memory trying to clone the backend name.\n"); + ctx->cond = CLI_WAIT_COND_BE_UNUSED; + } else if (*args[2]) { /* show the command's help either upon request (-h) or error */ err = "Usage: wait {-h|} [condition [args...]]\n" @@ -2165,9 +2173,15 @@ static int cli_io_handler_wait(struct appctx *appctx) /* here we should evaluate our waiting conditions, if any */ - if (ctx->cond == CLI_WAIT_COND_SRV_UNUSED) { - /* check if the server in args[0]/args[1] can be released now */ - ret = srv_check_for_deletion(ctx->args[0], ctx->args[1], NULL, NULL, &ctx->msg); + if (ctx->cond == CLI_WAIT_COND_SRV_UNUSED || + ctx->cond == CLI_WAIT_COND_BE_UNUSED) { + if (ctx->cond == CLI_WAIT_COND_SRV_UNUSED) { + /* check if the server in args[0]/args[1] can be released now */ + ret = srv_check_for_deletion(ctx->args[0], ctx->args[1], NULL, NULL, &ctx->msg); + } + else { + ret = be_check_for_deletion(ctx->args[0], NULL, &ctx->msg); + } if (ret < 0) { /* unrecoverable failure */