From 655290aa7a8b303e5eb27a0484a1316f394c91b2 Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov@tarantool.org>
Date: Wed, 19 Apr 2023 17:02:05 +0300
Subject: [PATCH] replication: always reload URIs on reconfiguration

Even if the new value of box.cfg.replication is the same as the old one,
we still need to recreate appliers' IO stream contexts from the URIs on
reconfiguration, because a URI parameter may store a path to a file (for
example, an SSL certificate), which could change.

Needed for tarantool/tarantool-ee#432

NO_DOC=ee
NO_TEST=ee
NO_CHANGELOG=ee
---
 src/box/applier.cc      | 10 ++++++++++
 src/box/applier.h       |  8 ++++++++
 src/box/box.cc          |  5 ++++-
 src/box/replication.cc  | 10 ++++++++++
 src/box/replication.h   | 13 +++++++++++++
 src/lib/core/iostream.c |  5 ++---
 src/lib/core/iostream.h | 11 +++++++++++
 7 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/src/box/applier.cc b/src/box/applier.cc
index a44347fe0b..8e219c0ac3 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -2694,6 +2694,16 @@ applier_delete(struct applier *applier)
 	free(applier);
 }
 
+void
+applier_reload_uri(struct applier *applier)
+{
+	struct iostream_ctx io_ctx;
+	if (iostream_ctx_create(&io_ctx, IOSTREAM_CLIENT, &applier->uri) != 0)
+		diag_raise();
+	iostream_ctx_destroy(&applier->io_ctx);
+	iostream_ctx_move(&applier->io_ctx, &io_ctx);
+}
+
 void
 applier_resume(struct applier *applier)
 {
diff --git a/src/box/applier.h b/src/box/applier.h
index 15c41ffe71..a16a782b3f 100644
--- a/src/box/applier.h
+++ b/src/box/applier.h
@@ -316,6 +316,14 @@ applier_new(const struct uri *uri);
 void
 applier_delete(struct applier *applier);
 
+/**
+ * Recreates the IO stream context from the applier's URI.
+ *
+ * Throws an exception on failure.
+ */
+void
+applier_reload_uri(struct applier *applier);
+
 /*
  * Resume execution of applier until \a state.
  */
diff --git a/src/box/box.cc b/src/box/box.cc
index bdf603b8fe..48f025d895 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1780,8 +1780,11 @@ box_set_replication(void)
 	if (unchanged) {
 		/*
 		 * No need to reconnect or sync in case the configuration is
-		 * the same.
+		 * the same. However, we should still reload the URIs because
+		 * a URI parameter may store a path to a file (for example,
+		 * an SSL certificate), which could change.
 		 */
+		replicaset_reload_uris();
 		return;
 	}
 	/*
diff --git a/src/box/replication.cc b/src/box/replication.cc
index 606a3b0754..eee635ceef 100644
--- a/src/box/replication.cc
+++ b/src/box/replication.cc
@@ -1091,6 +1091,16 @@ replicaset_connect(const struct uri_set *uris,
 	uri_set_copy(&replication_uris, uris);
 }
 
+void
+replicaset_reload_uris(void)
+{
+	replicaset_foreach(replica) {
+		struct applier *applier = replica->applier;
+		if (applier != NULL)
+			applier_reload_uri(applier);
+	}
+}
+
 bool
 replicaset_needs_rejoin(struct replica **master)
 {
diff --git a/src/box/replication.h b/src/box/replication.h
index dd0777fa3f..9f1014de78 100644
--- a/src/box/replication.h
+++ b/src/box/replication.h
@@ -585,6 +585,19 @@ void
 replicaset_connect(const struct uri_set *uris,
 		   bool connect_quorum, bool keep_connect);
 
+/**
+ * Reload replica URIs.
+ *
+ * Called on reconfiguration in case the remote peer URIs are the same.
+ * A URI parameter may store a path to a file (for example, an SSL
+ * certificate), which could change, so we need to recreate appliers'
+ * IO stream contexts in this case.
+ *
+ * Throws an exception on failure.
+ */
+void
+replicaset_reload_uris(void);
+
 /**
  * Check if the current instance fell too much behind its
  * peers in the replica set and needs to be rebootstrapped.
diff --git a/src/lib/core/iostream.c b/src/lib/core/iostream.c
index f53a86906a..ca3f92e228 100644
--- a/src/lib/core/iostream.c
+++ b/src/lib/core/iostream.c
@@ -98,15 +98,14 @@ iostream_ctx_create(struct iostream_ctx *ctx, enum iostream_mode mode,
 {
 	assert(mode == IOSTREAM_SERVER || mode == IOSTREAM_CLIENT);
 	ctx->mode = mode;
+	ctx->ssl = NULL;
 	const char *transport = uri_param(uri, "transport", 0);
 	if (transport != NULL) {
 		if (strcmp(transport, "ssl") == 0) {
 			ctx->ssl = ssl_iostream_ctx_new(mode, uri);
 			if (ctx->ssl == NULL)
 				goto err;
-		} else if (strcmp(transport, "plain") == 0) {
-			ctx->ssl = NULL;
-		} else {
+		} else if (strcmp(transport, "plain") != 0) {
 			diag_set(IllegalParams, "Invalid transport: %s",
 				 transport);
 			goto err;
diff --git a/src/lib/core/iostream.h b/src/lib/core/iostream.h
index 822d0c8d4c..31951cc2d2 100644
--- a/src/lib/core/iostream.h
+++ b/src/lib/core/iostream.h
@@ -265,6 +265,17 @@ iostream_ctx_clear(struct iostream_ctx *ctx)
 	ctx->ssl = NULL;
 }
 
+/**
+ * Move constructor: copies src to dst and clears src.
+ */
+static inline void
+iostream_ctx_move(struct iostream_ctx *dst, struct iostream_ctx *src)
+{
+	assert(src->mode == IOSTREAM_CLIENT || src->mode == IOSTREAM_SERVER);
+	*dst = *src;
+	iostream_ctx_clear(src);
+}
+
 /**
  * Creates an IO stream context for the given mode and URI.
  * On success returns 0. On failure returns -1, sets diag,
-- 
GitLab