|
@@ -24,14 +24,20 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
|
import static org.mockito.ArgumentMatchers.anyString;
|
|
|
import static org.mockito.Mockito.doAnswer;
|
|
|
+import static org.mockito.Mockito.doReturn;
|
|
|
import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.spy;
|
|
|
import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
import static org.mockito.Mockito.when;
|
|
|
import java.io.File;
|
|
|
import java.io.IOException;
|
|
|
import java.net.InetAddress;
|
|
|
import java.net.InetSocketAddress;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.Map;
|
|
|
import org.apache.jute.OutputArchive;
|
|
|
import org.apache.jute.Record;
|
|
@@ -42,6 +48,7 @@ import org.apache.zookeeper.server.ZKDatabase;
|
|
|
import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
|
|
|
import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType;
|
|
|
import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
|
|
|
+import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
|
|
|
import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier;
|
|
|
import org.apache.zookeeper.txn.TxnHeader;
|
|
|
import org.junit.jupiter.api.AfterEach;
|
|
@@ -222,4 +229,64 @@ public class LeaderBeanTest {
|
|
|
assertEquals("5\n", leaderBean.nonVotingFollowerInfo());
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void testGetDesignatedLeaderShouldRecreateSocketAddresses() {
|
|
|
+ Leader.Proposal p = new Leader.Proposal();
|
|
|
+ Map<Long, QuorumServer> peersView = new HashMap<>();
|
|
|
+ QuorumServer qs = Mockito.mock(QuorumServer.class);
|
|
|
+ MultipleAddresses multipleAddresses = new MultipleAddresses();
|
|
|
+ qs.type = LearnerType.PARTICIPANT;
|
|
|
+ qs.addr = multipleAddresses;
|
|
|
+ peersView.put(0L, qs);
|
|
|
+ QuorumVerifier qv = new QuorumMaj(peersView);
|
|
|
+ HashSet<Long> ackset = new HashSet<>();
|
|
|
+ ackset.add(0L);
|
|
|
+ ArrayList<Leader.Proposal.QuorumVerifierAcksetPair> qvAcksetPairs = new ArrayList<>();
|
|
|
+ qvAcksetPairs.add(new Leader.Proposal.QuorumVerifierAcksetPair(qv, ackset));
|
|
|
+ p.qvAcksetPairs = qvAcksetPairs;
|
|
|
+ Leader spyLeader = spy(leader);
|
|
|
+ doReturn(multipleAddresses, multipleAddresses).when(spyLeader).recreateSocketAddresses(any(MultipleAddresses.class));
|
|
|
+
|
|
|
+ spyLeader.getDesignatedLeader(p, 0L);
|
|
|
+ // Verify `recreateSocketAddresses` method should be invoked twice for the address in proposal and self one
|
|
|
+ verify(spyLeader, times(2)).recreateSocketAddresses(any(MultipleAddresses.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRecreateSocketAddresses() {
|
|
|
+ InetAddress loopback = InetAddress.getLoopbackAddress();
|
|
|
+ String oldIP = loopback.getHostAddress();
|
|
|
+ String newIP = "1.1.1.1";
|
|
|
+
|
|
|
+ // test case 1: empty MultipleAddresses instance will still be empty after recreateSocketAddresses
|
|
|
+ MultipleAddresses multipleAddresses = new MultipleAddresses();
|
|
|
+ assertEquals(multipleAddresses, leader.recreateSocketAddresses(multipleAddresses));
|
|
|
+
|
|
|
+ // test case 2: The content of MultipleAddresses instance will still be the same after recreateSocketAddresses if address no change
|
|
|
+ InetSocketAddress addr1 = new InetSocketAddress(loopback, PortAssignment.unique());
|
|
|
+ InetSocketAddress addr2 = new InetSocketAddress(loopback, PortAssignment.unique());
|
|
|
+ multipleAddresses = new MultipleAddresses(Arrays.asList(addr1, addr2));
|
|
|
+ // Verify after recreateSocketAddresses, the multipleAddresses should be the same (i.e. under no DNS's interaction)
|
|
|
+ assertEquals(multipleAddresses, leader.recreateSocketAddresses(multipleAddresses));
|
|
|
+
|
|
|
+ // test case 3: Simulating the DNS returning different IP address for the same hostname during recreation.
|
|
|
+ // After recreateSocketAddresses, the MultipleAddresses should contain the updated IP address instance while other fields unchanged.
|
|
|
+ InetAddress spyInetAddr = Mockito.spy(loopback);
|
|
|
+ InetSocketAddress addr3 = new InetSocketAddress(spyInetAddr, PortAssignment.unique());
|
|
|
+ // Verify the address is the old IP before recreateSocketAddresses.
|
|
|
+ assertEquals(oldIP, addr3.getAddress().getHostAddress());
|
|
|
+ multipleAddresses = new MultipleAddresses(Arrays.asList(addr3));
|
|
|
+ // simulating the DNS returning different IP address
|
|
|
+ when(spyInetAddr.getHostAddress()).thenReturn(newIP);
|
|
|
+
|
|
|
+ // Verify after recreateSocketAddresses, the multipleAddresses should have different IP address result
|
|
|
+ MultipleAddresses newMultipleAddress = leader.recreateSocketAddresses(multipleAddresses);
|
|
|
+ assertNotEquals(multipleAddresses, newMultipleAddress);
|
|
|
+ assertEquals(1, multipleAddresses.getAllAddresses().size());
|
|
|
+ InetSocketAddress newAddr = multipleAddresses.getAllAddresses().iterator().next();
|
|
|
+ // Verify the hostName should still be the same
|
|
|
+ assertEquals(loopback.getHostName(), newAddr.getAddress().getHostName());
|
|
|
+ // Verify the IP address has changed.
|
|
|
+ assertEquals(newIP, newAddr.getAddress().getHostAddress());
|
|
|
+ }
|
|
|
}
|