]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cli/wait: add a condition to wait on a server to become unused
authorWilly Tarreau <w@1wt.eu>
Fri, 9 Feb 2024 19:35:52 +0000 (20:35 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 Feb 2024 19:38:08 +0000 (20:38 +0100)
The "wait" command now supports a condition, "srv-unused", which waits
for the designated server to become totally unused, indicating that it
is removable. Upon each wakeup it calls srv_check_for_deletion() to
verify if conditions are met, if not if it's recoverable, or if it's
not recoverable, and proceeds according to this, never waiting for a
final decision longer than the configured delay.

The purpose is to make it possible to remove servers from the CLI after
waiting for their sessions to be terminated:

  $ socat -t5 /path/to/socket - <<< "
        disable server px/srv1
        shutdown sessions server px/srv1
        wait 2s srv-unused px/srv1
        del server px/srv1"

Or even wait for connections to terminate themselves:

  $ socat -t70 /path/to/socket - <<< "
        disable server px/srv1
        wait 1m srv-unused px/srv1
        del server px/srv1"

doc/management.txt
include/haproxy/cli-t.h
src/cli.c

index dfd8eee2f14d2e8fe8ee108228b76d227b9d66be..eb534d0518f54280656ace0e425ee2996d4abca8 100644 (file)
@@ -3997,15 +3997,36 @@ update ssl ocsp-response <certfile>
   local tree, its contents will be displayed on the standard output. The format
   is the same as the one described in "show ssl ocsp-response".
 
-wait { -h | <delay> }
-  This simply waits for the requested delay before continuing. This can be used
-  to collect metrics around a specific interval. The default unit for the delay
-  is milliseconds, though other units are accepted if suffixed with the usual
-  timer units (us, ms, s, m, h, d). When used with the 'socat' utility, do not
-  forget to extend socat's close timeout to cover the wait time. Passing "-h"
-  as the first or second argument provides the command's usage.
+wait { -h | <delay> } [<condition> [<args>...]]
+  In its simplest form without any condition, this simply waits for the
+  requested delay before continuing. This can be used to collect metrics around
+  a specific interval.
+
+  With a condition and optional arguments, the command will wait for the
+  specified condition to be satisfied, to unrecoverably fail, or to remain
+  unsatisfied for the whole <delay> duration. The supported conditions are:
+
+  - srv-unused <proxy>/<server> : this will wait for the specified server to be
+    removable, i.e. be in maintenance and no longer have any connection on it.
+    Some conditions will never be accepted (e.g. not in maintenance) and will
+    cause the report of a specific error message indicating what condition is
+    not met. The server might even have been removed in parallel and no longer
+    exit. If everything is OK before the delay, a success is returned and the
+    operation is terminated.
+
+  The default unit for the delay is milliseconds, though other units are
+  accepted if suffixed with the usual timer units (us, ms, s, m, h, d). When
+  used with the 'socat' utility, do not forget to extend socat's close timeout
+  to cover the wait time. Passing "-h" as the first or second argument provides
+  the command's usage.
   Example:
-    $ socat -t20 /path/to/socket <<< "show activity; wait 10s; show activity"
+    $ socat -t20 /path/to/socket - <<< "show activity; wait 10s; show activity"
+
+    $ socat -t5 /path/to/socket - <<< "
+        disable server px/srv1
+        shutdown sessions server px/srv1
+        wait 2s srv-unused px/srv1
+        del server px/srv1"
 
 
 9.4. Master CLI
index 4a11893ed6d487c879a43445f2d9459d6c2223ce..9a33a9445a5abb05e52363cd7249da63f6939e75 100644 (file)
@@ -95,6 +95,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
 };
 
 struct cli_wait_ctx {
index 786f2a24e60961abf201cc2b0b9649d120ab0db5..f3b015ab0e63b035958b18cc5996c687a7ea0d7a 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -2035,14 +2035,40 @@ static int cli_parse_wait(char **args, char *payload, struct appctx *appctx, voi
                        return cli_err(appctx, "Invalid duration.\n");
        }
 
-       if (*args[2]) {
+       if (strcmp(args[2], "srv-unused") == 0) {
+               struct ist be_name, sv_name;
+
+               if (!*args[3])
+                       return cli_err(appctx, "Missing server name (<backend>/<server>).\n");
+
+               sv_name = ist(args[3]);
+               be_name = istsplit(&sv_name, '/');
+               if (!istlen(sv_name))
+                       return cli_err(appctx, "Require 'backend/server'.\n");
+
+               be_name = istdup(be_name);
+               sv_name = istdup(sv_name);
+               if (!isttest(be_name) || !isttest(sv_name)) {
+                       free(istptr(be_name));
+                       free(istptr(sv_name));
+                       return cli_err(appctx, "Out of memory trying to clone the server name.\n");
+               }
+
+               ctx->args[0] = ist0(be_name);
+               ctx->args[1] = ist0(sv_name);
+               ctx->cond = CLI_WAIT_COND_SRV_UNUSED;
+       }
+       else if (*args[2]) {
                /* show the command's help either upon request (-h) or error */
                err = "Usage: wait {-h|<duration>} [condition [args...]]\n"
                        "  - '-h' displays this help\n"
                        "  - <duration> is the maximum wait time, optionally suffixed by the unit among\n"
                        "    'us', 'ms', 's', 'm', 'h', and 'd'. ; the default unit is milliseconds.\n"
-                       "  - <condition> indicates what to wait for. By default, no events aborts the\n"
-                       "    operation, which makes it reliably pause for the specified duration.\n";
+                       "  - <condition> indicates what to wait for, no longer than the specified\n"
+                       "    duration. Supported conditions are:\n"
+                       "    - <none> : by default, just sleep for the specified duration.\n"
+                       "    - srv-unused <px>/<sv> : wait for this server to become unused.\n"
+                       "";
 
                if (strcmp(args[2], "-h") == 0)
                        return cli_msg(appctx, LOG_INFO, err);
@@ -2069,6 +2095,7 @@ static int cli_io_handler_wait(struct appctx *appctx)
        struct cli_wait_ctx *ctx = appctx->svcctx;
        struct stconn *sc = appctx_sc(appctx);
        uint total, elapsed, left, wait;
+       int ret;
 
        /* note: upon first invocation, the timeout is not set */
        if (tick_isset(appctx->t->expire) &&
@@ -2077,6 +2104,24 @@ 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 */
+               thread_isolate();
+               ret = srv_check_for_deletion(ctx->args[0], ctx->args[1], NULL, NULL, NULL);
+               thread_release();
+
+               if (ret < 0) {
+                       /* unrecoverable failure */
+                       ctx->error = CLI_WAIT_ERR_FAIL;
+                       return 1;
+               } else if (ret > 0) {
+                       /* immediate success */
+                       ctx->error = CLI_WAIT_ERR_DONE;
+                       return 1;
+               }
+               /* let's check the timer */
+       }
+
        /* and here we recalculate the new wait time or abort */
        left  = tick_remain(now_ms, ctx->deadline);
        if (!left) {
@@ -3530,7 +3575,7 @@ static struct cli_kw_list cli_kws = {{ },{
        { { "show", "version", NULL },           "show version                            : show version of the current process",                     cli_parse_show_version, NULL, NULL, NULL, ACCESS_MASTER },
        { { "operator", NULL },                  "operator                                : lower the level of the current CLI session to operator",  cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
        { { "user", NULL },                      "user                                    : lower the level of the current CLI session to user",      cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
-       { { "wait", NULL },                      "wait {-h|<delay_ms>}                    : wait the specified delay (-h to see usage)",              cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL },
+       { { "wait", NULL },                      "wait {-h|<delay_ms>} cond [args...]     : wait the specified delay or condition (-h to see list)",  cli_parse_wait, cli_io_handler_wait, cli_release_wait, NULL },
        {{},}
 }};