From d6547bba8cfb857ff79939447c72318714bc1f90 Mon Sep 17 00:00:00 2001
From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Date: Tue, 28 Feb 2023 23:15:04 +0100
Subject: [PATCH] replication: fix outdated ballot on deanon failure

If attempt to set `box.cfg{replication_anon = false}` failed, the
instance's ballot event had is_anon = false nonetheless. This was
because on reconfig failure the option's scope guard did revert
the option itself in C++ code, but didn't update the ballot.

NO_DOC=bugfix
---
 .../unreleased/anon-on-failure-ballot.md      |  4 ++
 src/box/box.cc                                |  1 +
 test/replication-luatest/anon_test.lua        | 61 +++++++++++++++++++
 3 files changed, 66 insertions(+)
 create mode 100644 changelogs/unreleased/anon-on-failure-ballot.md
 create mode 100644 test/replication-luatest/anon_test.lua

diff --git a/changelogs/unreleased/anon-on-failure-ballot.md b/changelogs/unreleased/anon-on-failure-ballot.md
new file mode 100644
index 0000000000..f01ee0eead
--- /dev/null
+++ b/changelogs/unreleased/anon-on-failure-ballot.md
@@ -0,0 +1,4 @@
+## bugfix/replication
+
+* Fixed a bug when new instances could try to register via an anon instance
+  which previously failed to apply `box.cfg{replication_anon = false}`.
diff --git a/src/box/box.cc b/src/box/box.cc
index 28ab980232..a489dd5614 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1970,6 +1970,7 @@ box_set_replication_anon(void)
 	if (!anon) {
 		auto guard = make_scoped_guard([&]{
 			replication_anon = !anon;
+			box_broadcast_ballot();
 		});
 		/* Turn anonymous instance into a normal one. */
 		replication_anon = anon;
diff --git a/test/replication-luatest/anon_test.lua b/test/replication-luatest/anon_test.lua
new file mode 100644
index 0000000000..750bf4003f
--- /dev/null
+++ b/test/replication-luatest/anon_test.lua
@@ -0,0 +1,61 @@
+local server = require('luatest.server')
+local t = require('luatest')
+local g = t.group()
+
+g.before_all = function(lg)
+    lg.master = server:new({alias = 'master'})
+    lg.master:start()
+end
+
+g.after_all = function(lg)
+    lg.master:drop()
+end
+
+--
+-- When an instance failed to apply cfg{replication_anon = false}, it used to
+-- report itself as non-anon in the ballot anyway. Shouldn't be so.
+--
+g.test_ballot_on_deanon_fail = function(lg)
+    local box_cfg = {
+        replication_anon = true,
+        read_only = true,
+        replication = {
+            lg.master.net_box_uri,
+        },
+    }
+    local replica = server:new({
+        alias = 'replica',
+        box_cfg = box_cfg,
+    })
+    replica:start()
+    replica:exec(function()
+        t.assert_equals(box.info.id, 0)
+    end)
+    lg.master:exec(function()
+        rawset(_G, 'test_trigger', function()
+            error('Reject _cluster update')
+        end)
+        box.space._cluster:on_replace(_G.test_trigger)
+    end)
+    replica:exec(function()
+        local fiber = require('fiber')
+        local iproto = box.iproto
+        local is_anon = nil
+        local w = box.watch('internal.ballot', function(_, value)
+            is_anon = value[iproto.key.BALLOT][iproto.ballot_key.IS_ANON]
+        end)
+        fiber.yield()
+        t.assert(is_anon)
+        is_anon = nil
+        t.assert_error_msg_contains('Reject _cluster update', box.cfg,
+                                    {replication_anon = false})
+        fiber.yield()
+        t.assert(is_anon)
+        w:unregister()
+    end)
+    replica:drop()
+    lg.master:exec(function()
+        box.space._cluster:on_replace(nil, _G.test_trigger)
+        _G.test_trigger = nil
+    end)
+end
-- 
GitLab