]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: dns/server: fix incomatibility between SRV resolution and server state...
authorBaptiste Assmann <bedis9@gmail.com>
Mon, 2 Jul 2018 15:00:54 +0000 (17:00 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 4 Sep 2018 15:40:22 +0000 (17:40 +0200)
Server state file has no indication that a server is currently managed
by a DNS SRV resolution.
And thus, both feature (DNS SRV resolution and server state), when used
together, does not provide the expected behavior: a smooth experience...

This patch introduce the "SRV record name" in the server state file and
loads and applies it if found and wherever required.

This patch applies to haproxy-dev branch only. For backport, a specific patch
is provided for 1.8.

doc/management.txt
include/types/server.h
src/proxy.c
src/server.c

index c78dc7486225ddf0ab465309a324d91c34d055cc..17f15059e8bbb0ea494cc6ccb6e5776c4ab2a3dc 100644 (file)
@@ -2130,6 +2130,7 @@ show servers state [<backend>]
                                   configuration.
      srv_fqdn:                    Server FQDN.
      srv_port:                    Server port.
+     srvrecord:                   DNS SRV record associated to this SRV.
 
 show sess
   Dump all known sessions. Avoid doing this on slow connections as this can
index 88259281d14d83a9010e46d2fedd19edbc755a7d..8adc29baf674612256949ec8bcea42be57d392d8 100644 (file)
@@ -126,10 +126,11 @@ enum srv_initaddr {
     "bk_f_forced_id "             \
     "srv_f_forced_id "            \
     "srv_fqdn "                   \
-    "srv_port"
+    "srv_port "                   \
+    "srvrecord"
 
-#define SRV_STATE_FILE_MAX_FIELDS 19
-#define SRV_STATE_FILE_NB_FIELDS_VERSION_1 18
+#define SRV_STATE_FILE_MAX_FIELDS 20
+#define SRV_STATE_FILE_NB_FIELDS_VERSION_1 19
 #define SRV_STATE_LINE_MAXLEN 512
 
 /* server flags -- 32 bits */
index 73aa1187a78e91a4af9ab7444f6b3f59af2501c8..c939e820c78c826b86e327d6927e4dd0c00642d2 100644 (file)
@@ -1429,6 +1429,7 @@ static int dump_servers_state(struct stream_interface *si, struct buffer *buf)
        char srv_addr[INET6_ADDRSTRLEN + 1];
        time_t srv_time_since_last_change;
        int bk_f_forced_id, srv_f_forced_id;
+       char *srvrecord;
 
        /* we don't want to report any state if the backend is not enabled on this process */
        if (px->bind_proc && !(px->bind_proc & pid_bit))
@@ -1458,18 +1459,23 @@ static int dump_servers_state(struct stream_interface *si, struct buffer *buf)
                bk_f_forced_id = px->options & PR_O_FORCED_ID ? 1 : 0;
                srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0;
 
+               srvrecord = NULL;
+               if (srv->srvrq && srv->srvrq->name)
+                       srvrecord = srv->srvrq->name;
+
                chunk_appendf(buf,
                                "%d %s "
                                "%d %s %s "
                                "%d %d %d %d %ld "
                                "%d %d %d %d %d "
-                               "%d %d %s %u"
+                               "%d %d %s %u %s"
                                "\n",
                                px->uuid, px->id,
                                srv->puid, srv->id, srv_addr,
                                srv->cur_state, srv->cur_admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
                                srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
-                               bk_f_forced_id, srv_f_forced_id, srv->hostname ? srv->hostname : "-", srv->svc_port);
+                               bk_f_forced_id, srv_f_forced_id, srv->hostname ? srv->hostname : "-", srv->svc_port,
+                               srvrecord ? srvrecord : "-");
                if (ci_putchk(si_ic(si), &trash) == -1) {
                        si_applet_cant_put(si);
                        return 0;
index 78d5a0fcc3000890a133b1706052201b6577dcb0..766f5ded1cf3d177b2d2d1b7d1d72b071d333173 100644 (file)
@@ -2758,6 +2758,7 @@ static void srv_update_state(struct server *srv, int version, char **params)
        const char *fqdn;
        const char *port_str;
        unsigned int port;
+       char *srvrecord;
 
        fqdn = NULL;
        port = 0;
@@ -2781,6 +2782,7 @@ static void srv_update_state(struct server *srv, int version, char **params)
                         * srv_f_forced_id:      params[12]
                         * srv_fqdn:             params[13]
                         * srv_port:             params[14]
+                        * srvrecord:            params[15]
                         */
 
                        /* validating srv_op_state */
@@ -2913,6 +2915,13 @@ static void srv_update_state(struct server *srv, int version, char **params)
                                }
                        }
 
+                       /* SRV record
+                        * NOTE: in HAProxy, SRV records must start with an underscore '_'
+                        */
+                       srvrecord = params[15];
+                       if (srvrecord && *srvrecord != '_')
+                               srvrecord = NULL;
+
                        /* don't apply anything if one error has been detected */
                        if (msg->data)
                                goto out;
@@ -3045,6 +3054,48 @@ static void srv_update_state(struct server *srv, int version, char **params)
                                        }
                                }
                        }
+                       /* If all the conditions below are validated, this means
+                        * we're evaluating a server managed by SRV resolution
+                        */
+                       else if (fqdn && !srv->hostname && srvrecord) {
+                               int res;
+
+                               /* we can't apply previous state if SRV record has changed */
+                               if (srv->srvrq && strcmp(srv->srvrq->name, srvrecord) != 0) {
+                                       chunk_appendf(msg, ", SRV record mismatch between configuration ('%s') and state file ('%s) for server '%s'. Previous state not applied", srv->srvrq->name, srvrecord, srv->id);
+                                       HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+                                       goto out;
+                               }
+
+                               /* create or find a SRV resolution for this srv record */
+                               if (srv->srvrq == NULL && (srv->srvrq = find_srvrq_by_name(srvrecord, srv->proxy)) == NULL)
+                                       srv->srvrq = new_dns_srvrq(srv, srvrecord);
+                               if (srv->srvrq == NULL) {
+                                       chunk_appendf(msg, ", can't create or find SRV resolution '%s' for server '%s'", srvrecord, srv->id);
+                                       HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+                                       goto out;
+                               }
+
+                               /* prepare DNS resolution for this server */
+                               res = srv_prepare_for_resolution(srv, fqdn);
+                               if (res == -1) {
+                                       chunk_appendf(msg, ", can't allocate memory for DNS resolution for server '%s'", srv->id);
+                                       HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock);
+                                       goto out;
+                               }
+
+                               /* configure check.port accordingly */
+                               if ((srv->check.state & CHK_ST_CONFIGURED) &&
+                                   !(srv->flags & SRV_F_CHECKPORT))
+                                       srv->check.port = port;
+
+                               /* Unset SRV_F_MAPPORTS for SRV records.
+                                * SRV_F_MAPPORTS is unfortunately set by parse_server()
+                                * because no ports are provided in the configuration file.
+                                * This is because HAProxy will use the port found into the SRV record.
+                                */
+                               srv->flags &= ~SRV_F_MAPPORTS;
+                       }
 
                        if (port_str)
                                srv->svc_port = port;
@@ -3301,6 +3352,7 @@ void apply_server_state(void)
                                                         * srv_f_forced_id:      params[16] => srv_params[12]
                                                         * srv_fqdn:             params[17] => srv_params[13]
                                                         * srv_port:             params[18] => srv_params[14]
+                                                        * srvrecord:            params[19] => srv_params[15]
                                                         */
                                                        if (arg >= 4) {
                                                                srv_params[srv_arg] = cur;