|
@@ -1,140 +1,238 @@
|
|
-/**
|
|
|
|
- * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
- * or more contributor license agreements. See the NOTICE file
|
|
|
|
- * distributed with this work for additional information
|
|
|
|
- * regarding copyright ownership. The ASF licenses this file
|
|
|
|
- * to you under the Apache License, Version 2.0 (the
|
|
|
|
- * "License"); you may not use this file except in compliance
|
|
|
|
- * with the License. You may obtain a copy of the License at
|
|
|
|
- *
|
|
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
- *
|
|
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
|
|
- * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
- * See the License for the specific language governing permissions and
|
|
|
|
- * limitations under the License.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-package org.apache.zookeeper.server.quorum;
|
|
|
|
-
|
|
|
|
-import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
|
|
|
|
-
|
|
|
|
-import java.io.File;
|
|
|
|
-import java.io.FileInputStream;
|
|
|
|
-import java.io.FileNotFoundException;
|
|
|
|
-import java.io.IOException;
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
-import java.util.Properties;
|
|
|
|
-
|
|
|
|
-import org.apache.zookeeper.PortAssignment;
|
|
|
|
-import org.apache.zookeeper.ZooKeeper;
|
|
|
|
-import org.apache.zookeeper.test.ClientBase;
|
|
|
|
-import org.apache.zookeeper.test.ReconfigTest;
|
|
|
|
-import org.junit.Assert;
|
|
|
|
-import org.junit.Test;
|
|
|
|
-
|
|
|
|
-public class DynamicConfigBackwardCompatibilityTest extends QuorumPeerTestBase {
|
|
|
|
- /**
|
|
|
|
- * This test checks that when started with a single static config file the
|
|
|
|
- * servers will create a valid dynamic config file.
|
|
|
|
- */
|
|
|
|
- @Test
|
|
|
|
- public void testConfigFileBackwardCompatibility() throws Exception {
|
|
|
|
- ClientBase.setupTestEnv();
|
|
|
|
-
|
|
|
|
- final int SERVER_COUNT = 3;
|
|
|
|
- final int clientPorts[] = new int[SERVER_COUNT];
|
|
|
|
- StringBuilder sb = new StringBuilder();
|
|
|
|
- String server;
|
|
|
|
- ArrayList<String> allServers = new ArrayList<String>();
|
|
|
|
-
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- clientPorts[i] = PortAssignment.unique();
|
|
|
|
- server = "server." + i + "=localhost:" + PortAssignment.unique()
|
|
|
|
- + ":" + PortAssignment.unique() + ":participant;localhost:"
|
|
|
|
- + clientPorts[i];
|
|
|
|
- allServers.add(server);
|
|
|
|
- sb.append(server + "\n");
|
|
|
|
- }
|
|
|
|
- String currentQuorumCfgSection = sb.toString();
|
|
|
|
-
|
|
|
|
- MainThread mt[] = new MainThread[SERVER_COUNT];
|
|
|
|
- ZooKeeper zk[] = new ZooKeeper[SERVER_COUNT];
|
|
|
|
-
|
|
|
|
- // Start the servers with the static config file, without the dynamic
|
|
|
|
- // config file.
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection,
|
|
|
|
- false);
|
|
|
|
- // check that a dynamic configuration file wasn't created
|
|
|
|
- Assert.assertFalse(mt[i].dynamicConfigFile.exists());
|
|
|
|
- mt[i].start();
|
|
|
|
- }
|
|
|
|
- // Check that the servers are up, have the right config and can process
|
|
|
|
- // operations
|
|
|
|
- // Check that the static config was split to static and dynamic files
|
|
|
|
- // correctly
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- Assert.assertTrue("waiting for server " + i + " being up",
|
|
|
|
- ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
|
|
|
|
- CONNECTION_TIMEOUT));
|
|
|
|
- zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i],
|
|
|
|
- ClientBase.CONNECTION_TIMEOUT, this);
|
|
|
|
- Assert.assertTrue(mt[i].dynamicConfigFile.exists());
|
|
|
|
- ReconfigTest.testServerHasConfig(zk[i], allServers, null);
|
|
|
|
- // check that static config file doesn't include membership info
|
|
|
|
- // and has a pointer to the dynamic configuration file
|
|
|
|
- Properties cfg = readPropertiesFromFile(mt[i].confFile);
|
|
|
|
- for (int j = 0; j < SERVER_COUNT; j++) {
|
|
|
|
- Assert.assertFalse(cfg.containsKey("server." + j));
|
|
|
|
- }
|
|
|
|
- Assert.assertTrue(cfg.containsKey("dynamicConfigFile"));
|
|
|
|
- Assert.assertFalse(cfg.containsKey("clientPort"));
|
|
|
|
-
|
|
|
|
- // check that the dynamic configuration file contains the membership
|
|
|
|
- // info
|
|
|
|
- cfg = readPropertiesFromFile(mt[i].dynamicConfigFile);
|
|
|
|
- for (int j = 0; j < SERVER_COUNT; j++) {
|
|
|
|
- String serverLine = cfg.getProperty("server." + j, "");
|
|
|
|
- Assert.assertEquals(allServers.get(j), "server." + j + "="
|
|
|
|
- + serverLine);
|
|
|
|
- }
|
|
|
|
- Assert.assertFalse(cfg.containsKey("dynamicConfigFile"));
|
|
|
|
- }
|
|
|
|
- ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
-
|
|
|
|
- // now shut down the servers and restart them
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- zk[i].close();
|
|
|
|
- mt[i].shutdown();
|
|
|
|
- }
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- mt[i].start();
|
|
|
|
- }
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- Assert.assertTrue("waiting for server " + i + " being up",
|
|
|
|
- ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
|
|
|
|
- CONNECTION_TIMEOUT));
|
|
|
|
- zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i],
|
|
|
|
- ClientBase.CONNECTION_TIMEOUT, this);
|
|
|
|
- ReconfigTest.testServerHasConfig(zk[i], allServers, null);
|
|
|
|
- }
|
|
|
|
- ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
- for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
- mt[i].shutdown();
|
|
|
|
- zk[i].close();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private Properties readPropertiesFromFile(File file) throws IOException {
|
|
|
|
- Properties cfg = new Properties();
|
|
|
|
- FileInputStream in = new FileInputStream(file);
|
|
|
|
- try {
|
|
|
|
- cfg.load(in);
|
|
|
|
- } finally {
|
|
|
|
- in.close();
|
|
|
|
- }
|
|
|
|
- return cfg;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
|
|
+/**
|
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
|
+ * distributed with this work for additional information
|
|
|
|
+ * regarding copyright ownership. The ASF licenses this file
|
|
|
|
+ * to you under the Apache License, Version 2.0 (the
|
|
|
|
+ * "License"); you may not use this file except in compliance
|
|
|
|
+ * with the License. You may obtain a copy of the License at
|
|
|
|
+ *
|
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
+ *
|
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
|
+ * limitations under the License.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+package org.apache.zookeeper.server.quorum;
|
|
|
|
+
|
|
|
|
+import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT;
|
|
|
|
+
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.FileInputStream;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Properties;
|
|
|
|
+
|
|
|
|
+import org.apache.zookeeper.PortAssignment;
|
|
|
|
+import org.apache.zookeeper.ZooKeeper;
|
|
|
|
+import org.apache.zookeeper.test.ClientBase;
|
|
|
|
+import org.apache.zookeeper.test.ReconfigTest;
|
|
|
|
+import org.junit.Assert;
|
|
|
|
+import org.junit.Before;
|
|
|
|
+import org.junit.Test;
|
|
|
|
+
|
|
|
|
+public class ReconfigLegacyTest extends QuorumPeerTestBase {
|
|
|
|
+
|
|
|
|
+ private static final int SERVER_COUNT = 3;
|
|
|
|
+
|
|
|
|
+ @Before
|
|
|
|
+ public void setup() {
|
|
|
|
+ ClientBase.setupTestEnv();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This test checks that when started with a single static config file the
|
|
|
|
+ * servers will create a valid dynamic config file. Also checks that when
|
|
|
|
+ * the static config includes a clientPort but the dynamic definition also
|
|
|
|
+ * includes it, the static definition is erased.
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testConfigFileBackwardCompatibility() throws Exception {
|
|
|
|
+ final int clientPorts[] = new int[SERVER_COUNT];
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
+ String server;
|
|
|
|
+ ArrayList<String> allServers = new ArrayList<String>();
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ clientPorts[i] = PortAssignment.unique();
|
|
|
|
+ server = "server." + i + "=localhost:" + PortAssignment.unique()
|
|
|
|
+ + ":" + PortAssignment.unique() + ":participant;localhost:"
|
|
|
|
+ + clientPorts[i];
|
|
|
|
+ allServers.add(server);
|
|
|
|
+ sb.append(server + "\n");
|
|
|
|
+ }
|
|
|
|
+ String currentQuorumCfgSection = sb.toString();
|
|
|
|
+
|
|
|
|
+ MainThread mt[] = new MainThread[SERVER_COUNT];
|
|
|
|
+ ZooKeeper zk[] = new ZooKeeper[SERVER_COUNT];
|
|
|
|
+
|
|
|
|
+ // Start the servers with a static config file, without a dynamic
|
|
|
|
+ // config file.
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false);
|
|
|
|
+ // check that a dynamic configuration file doesn't exist
|
|
|
|
+ Assert.assertFalse(mt[i].dynamicConfigFile.exists());
|
|
|
|
+ mt[i].start();
|
|
|
|
+ }
|
|
|
|
+ // Check that the servers are up, have the right config and can process operations.
|
|
|
|
+ // Check that the static config was split into static and dynamic files correctly.
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ Assert.assertTrue("waiting for server " + i + " being up",
|
|
|
|
+ ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ CONNECTION_TIMEOUT));
|
|
|
|
+ zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ ClientBase.CONNECTION_TIMEOUT, this);
|
|
|
|
+ Assert.assertTrue(mt[i].dynamicConfigFile.exists());
|
|
|
|
+ ReconfigTest.testServerHasConfig(zk[i], allServers, null);
|
|
|
|
+ // check that static config file doesn't include membership info
|
|
|
|
+ // and has a pointer to the dynamic configuration file
|
|
|
|
+ Properties cfg = readPropertiesFromFile(mt[i].confFile);
|
|
|
|
+ for (int j = 0; j < SERVER_COUNT; j++) {
|
|
|
|
+ Assert.assertFalse(cfg.containsKey("server." + j));
|
|
|
|
+ }
|
|
|
|
+ Assert.assertTrue(cfg.containsKey("dynamicConfigFile"));
|
|
|
|
+ Assert.assertFalse(cfg.containsKey("clientPort"));
|
|
|
|
+
|
|
|
|
+ // check that the dynamic configuration file contains the membership info
|
|
|
|
+ cfg = readPropertiesFromFile(mt[i].dynamicConfigFile);
|
|
|
|
+ for (int j = 0; j < SERVER_COUNT; j++) {
|
|
|
|
+ String serverLine = cfg.getProperty("server." + j, "");
|
|
|
|
+ Assert.assertEquals(allServers.get(j), "server." + j + "="
|
|
|
|
+ + serverLine);
|
|
|
|
+ }
|
|
|
|
+ Assert.assertFalse(cfg.containsKey("dynamicConfigFile"));
|
|
|
|
+ }
|
|
|
|
+ ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
+
|
|
|
|
+ // now shut down the servers and restart them
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ zk[i].close();
|
|
|
|
+ mt[i].shutdown();
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ mt[i].start();
|
|
|
|
+ }
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ Assert.assertTrue("waiting for server " + i + " being up",
|
|
|
|
+ ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ CONNECTION_TIMEOUT));
|
|
|
|
+ zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ ClientBase.CONNECTION_TIMEOUT, this);
|
|
|
|
+ ReconfigTest.testServerHasConfig(zk[i], allServers, null);
|
|
|
|
+ }
|
|
|
|
+ ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ mt[i].shutdown();
|
|
|
|
+ zk[i].close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1992}
|
|
|
|
+ * 1. When a server starts from old style static config, without a client port in the server
|
|
|
|
+ * specification, it should keep the client port in static config file.
|
|
|
|
+ * 2. After port reconfig, the old port should be removed from static file
|
|
|
|
+ * and new port added to dynamic file.
|
|
|
|
+ * @throws Exception
|
|
|
|
+ */
|
|
|
|
+ @Test
|
|
|
|
+ public void testReconfigRemoveClientFromStatic() throws Exception {
|
|
|
|
+ final int clientPorts[] = new int[SERVER_COUNT];
|
|
|
|
+ final int quorumPorts[] = new int[SERVER_COUNT];
|
|
|
|
+ final int electionPorts[] = new int[SERVER_COUNT];
|
|
|
|
+
|
|
|
|
+ final int changedServerId = 0;
|
|
|
|
+ final int newClientPort = PortAssignment.unique();
|
|
|
|
+
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
+ ArrayList<String> allServers = new ArrayList<String>();
|
|
|
|
+ ArrayList<String> newServers = new ArrayList<String>();
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ clientPorts[i] = PortAssignment.unique();
|
|
|
|
+ quorumPorts[i] = PortAssignment.unique();
|
|
|
|
+ electionPorts[i] = PortAssignment.unique();
|
|
|
|
+
|
|
|
|
+ String server = "server." + i + "=localhost:" + quorumPorts[i]
|
|
|
|
+ +":" + electionPorts[i] + ":participant";
|
|
|
|
+ allServers.add(server);
|
|
|
|
+ sb.append(server + "\n");
|
|
|
|
+
|
|
|
|
+ if(i == changedServerId) {
|
|
|
|
+ newServers.add(server + ";0.0.0.0:" + newClientPort);
|
|
|
|
+ } else {
|
|
|
|
+ newServers.add(server);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ String quorumCfgSection = sb.toString();
|
|
|
|
+
|
|
|
|
+ MainThread mt[] = new MainThread[SERVER_COUNT];
|
|
|
|
+ ZooKeeper zk[] = new ZooKeeper[SERVER_COUNT];
|
|
|
|
+
|
|
|
|
+ // Start the servers with a static config file, without a dynamic config file.
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ mt[i] = new MainThread(i, clientPorts[i], quorumCfgSection, false);
|
|
|
|
+ // check that a dynamic configuration file doesn't exist
|
|
|
|
+ Assert.assertFalse(mt[i].dynamicConfigFile.exists());
|
|
|
|
+ mt[i].start();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check that when a server starts from old style config, it should keep the client
|
|
|
|
+ // port in static config file.
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ Assert.assertTrue("waiting for server " + i + " being up",
|
|
|
|
+ ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ CONNECTION_TIMEOUT));
|
|
|
|
+ zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i],
|
|
|
|
+ ClientBase.CONNECTION_TIMEOUT, this);
|
|
|
|
+
|
|
|
|
+ ReconfigTest.testServerHasConfig(zk[i], allServers, null);
|
|
|
|
+ Properties cfg = readPropertiesFromFile(mt[i].confFile);
|
|
|
|
+
|
|
|
|
+ Assert.assertTrue(cfg.containsKey("dynamicConfigFile"));
|
|
|
|
+ Assert.assertTrue(cfg.containsKey("clientPort"));
|
|
|
|
+ }
|
|
|
|
+ ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
+
|
|
|
|
+ ReconfigTest.reconfig(zk[1], null, null, newServers, -1);
|
|
|
|
+ ReconfigTest.testNormalOperation(zk[0], zk[1]);
|
|
|
|
+
|
|
|
|
+ // Sleep since writing the config files may take time.
|
|
|
|
+ Thread.sleep(1000);
|
|
|
|
+
|
|
|
|
+ // Check that new dynamic config includes the updated client port.
|
|
|
|
+ // Check that server changedServerId erased clientPort from static config.
|
|
|
|
+ // Check that other servers still have clientPort in static config.
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ ReconfigTest.testServerHasConfig(zk[i], newServers, null);
|
|
|
|
+ Properties staticCfg = readPropertiesFromFile(mt[i].confFile);
|
|
|
|
+ if (i == changedServerId) {
|
|
|
|
+ Assert.assertFalse(staticCfg.containsKey("clientPort"));
|
|
|
|
+ } else {
|
|
|
|
+ Assert.assertTrue(staticCfg.containsKey("clientPort"));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < SERVER_COUNT; i++) {
|
|
|
|
+ mt[i].shutdown();
|
|
|
|
+ zk[i].close();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private Properties readPropertiesFromFile(File file) throws IOException {
|
|
|
|
+ Properties cfg = new Properties();
|
|
|
|
+ FileInputStream in = new FileInputStream(file);
|
|
|
|
+ try {
|
|
|
|
+ cfg.load(in);
|
|
|
|
+ } finally {
|
|
|
|
+ in.close();
|
|
|
|
+ }
|
|
|
|
+ return cfg;
|
|
|
|
+ }
|
|
|
|
+}
|