]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: proxy: define a basic "del backend" CLI
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 6 Jan 2026 10:40:05 +0000 (11:40 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 27 Feb 2026 09:28:24 +0000 (10:28 +0100)
Add "del backend" handler which is restricted to admin level. Along with
it, a new function be_check_for_deletion() is used to test if the
backend is removable.

doc/management.txt
include/haproxy/proxy.h
src/proxy.c

index cee9f5b56fe5b9a0ce35d8efb90ed490bc33f7e6..57179611e3f1ce089858fdb13ad2eb1aa38ed7e2 100644 (file)
@@ -2124,6 +2124,16 @@ del acl <acl> [<key>|#<ref>]
   listing the content of the acl. Note that if the reference <acl> is a name and
   is shared with a map, the entry will be also deleted in the map.
 
+del backend <name>
+  Removes the backend proxy with the name <name>.
+
+  This operation is only possible for TCP or HTTP proxies. To succeed, the
+  backend instance must have been first unpublished.
+
+  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").
+
 del map <map> [<key>|#<ref>]
   Delete all the map entries from the map <map> corresponding to the key <key>.
   <map> is the #<id> or the <name> returned by "show map". If the <ref> is used,
index 69860510f8ac205c7fb2c54956244bc1147688f7..fa19924f71d45b4f92b737873afc7ce5c767823a 100644 (file)
@@ -101,6 +101,8 @@ void free_server_rules(struct list *srules);
 int proxy_init_per_thr(struct proxy *px);
 int proxy_finalize(struct proxy *px, int *err_code);
 
+int be_check_for_deletion(const char *bename, struct proxy **pb, const char **pm);
+
 /*
  * This function returns a string containing the type of the proxy in a format
  * suitable for error messages, from its capabilities.
index 22b76e7ae646c540dd23424cb815fd0acd85633f..5bed1ae95adbb17cae62867edf4e6ebb50956bdd 100644 (file)
@@ -4970,6 +4970,99 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc
        return 1;
 }
 
+/* Test if the backend instance named <bename> can be deleted.
+ *
+ * Returns a positive integer if backend can be deleted. Else, 0 is returned if
+ * backend should be deletable after some delay. A negative value indicates
+ * that backend cannot be deleted without any external action.
+ *
+ * If <pb> is not NULL, it will be set to point to the backend instance if name
+ * is found. If <pm> is not NULL, it will be used on error to point to the
+ * description failure.
+ */
+int be_check_for_deletion(const char *bename, struct proxy **pb, const char **pm)
+{
+       struct proxy *be = NULL;
+       const char *msg = NULL;
+       int ret;
+
+       /* First, unrecoverable errors */
+       ret = -1;
+
+       if (!(be = proxy_be_by_name(bename))) {
+               msg = "No such backend.";
+               goto out;
+       }
+
+       if (be->cap & PR_CAP_FE) {
+               msg = "Cannot delete a listen section.";
+               goto out;
+       }
+
+       if (be->mode != PR_MODE_TCP && be->mode != PR_MODE_HTTP) {
+               msg = "Only TCP or HTTP proxies can be removed at runtime.";
+               goto out;
+       }
+
+       if (!(be->flags & PR_FL_BE_UNPUBLISHED)) {
+               msg = "Backend must be unpublished prior to its deletion.";
+               goto out;
+       }
+
+       /* Second, conditions that may change over time */
+       ret = 0;
+
+       if (be->beconn) {
+               msg = "Backend still has attached streams on it.";
+               goto out;
+       }
+
+       ret = 1;
+
+ out:
+       if (pb)
+               *pb = be;
+       if (pm)
+               *pm = msg;
+       return ret;
+}
+
+/* Handler for "delete backend". Runs under thread isolation. Always returns 1. */
+static int cli_parse_delete_backend(char **args, char *payload, struct appctx *appctx, void *private)
+{
+       struct proxy *px;
+       const char *msg;
+       char *be_name;
+       int ret;
+
+       if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+               return 1;
+
+       if (*args[3]) {
+               cli_err(appctx, "Usage: del backend <name>.\n");
+               return 1;
+       }
+
+       thread_isolate_full();
+
+       be_name = args[2];
+       ret = be_check_for_deletion(be_name, &px, &msg);
+       if (ret <= 0) {
+               cli_err(appctx, msg);
+               goto out;
+       }
+
+       thread_release();
+
+       ha_notice("Backend deleted.\n");
+       cli_umsg(appctx, LOG_INFO);
+       return 1;
+
+ out:
+       thread_release();
+       return 1;
+}
+
 /* Parses the "disable frontend" directive, it always returns 1.
  *
  * Grabs the proxy lock.
@@ -5292,6 +5385,7 @@ static int cli_io_handler_show_errors(struct appctx *appctx)
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
        { { "add", "backend", NULL },                       "add backend <backend>                   : add a new backend",                                              cli_parse_add_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
+       { { "del", "backend", NULL },                       "del backend <backend>                   : delete a backend",                                               cli_parse_delete_backend, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
        { { "disable", "frontend",  NULL },                 "disable frontend <frontend>             : temporarily disable specific frontend",                          cli_parse_disable_frontend, NULL, NULL },
        { { "enable", "frontend",  NULL },                  "enable frontend <frontend>              : re-enable specific frontend",                                    cli_parse_enable_frontend, NULL, NULL },
        { { "publish", "backend",  NULL },                  "publish backend <backend>               : mark backend as ready for traffic",                              cli_parse_publish_backend, NULL, NULL },