/*
* Copyright 2014-2016 CyberVision, Inc.
*
* Licensed 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.kaaproject.kaa.server.common.zk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.test.TestingCluster;
import org.apache.curator.test.Timing;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.kaaproject.kaa.server.common.zk.control.ControlNode;
import org.kaaproject.kaa.server.common.zk.control.ControlNodeListener;
import org.kaaproject.kaa.server.common.zk.gen.ConnectionInfo;
import org.kaaproject.kaa.server.common.zk.gen.ControlNodeInfo;
import org.kaaproject.kaa.server.common.zk.gen.LoadInfo;
import org.kaaproject.kaa.server.common.zk.gen.OperationsNodeInfo;
import org.kaaproject.kaa.server.common.zk.operations.OperationsNode;
import java.nio.ByteBuffer;
import java.util.Random;
public class ControlNodeIT {
private static final String ENDPOINT_NODE_HOST = "192.168.0.101";
private static final String SECONDARY_NODE_HOST = "192.168.0.2";
private static final String CONTROL_NODE_HOST = "192.168.0.1";
private CuratorFramework zkClient;
private TestingCluster cluster;
@Before
public void beforeTest() {
try {
cluster = new TestingCluster(3);
cluster.start();
zkClient = CuratorFrameworkFactory.newClient(cluster.getConnectString(), buildDefaultRetryPolicy());
zkClient.start();
} catch (Exception e) {
System.err.println("Unable to initialize cluster before test! " + e);
}
}
@After
public void afterTest() {
try {
zkClient.close();
cluster.close();
} catch (Exception e) {
System.err.println("Unable to shutdown cluster after test! " + e);
}
}
@Test
public void masterFailoverTest() throws Exception {
Timing timing = new Timing();
ControlNodeInfo controlNodeInfo = buildControlNodeInfo();
ControlNodeInfo secondaryNodeInfo = buildSecondaryNodeInfo();
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
endpointNode.start();
assertNull(endpointNode.getControlServerInfo());
ControlNode controlNode = new ControlNode(controlNodeInfo, zkClient);
assertFalse(controlNode.isMaster());
controlNode.start();
ControlNode secondaryNode = new ControlNode(secondaryNodeInfo, zkClient);
assertFalse(secondaryNode.isMaster());
secondaryNode.start();
timing.sleepABit();
assertTrue(controlNode.isMaster());
assertFalse(secondaryNode.isMaster());
assertNotNull(endpointNode.getControlServerInfo());
assertEquals(CONTROL_NODE_HOST, endpointNode.getControlServerInfo().getConnectionInfo().getThriftHost().toString());
controlNode.close();
timing.sleepABit();
assertNotNull(endpointNode.getControlServerInfo());
assertEquals(SECONDARY_NODE_HOST, endpointNode.getControlServerInfo().getConnectionInfo().getThriftHost().toString());
secondaryNode.close();
endpointNode.close();
}
@Test
public void masterListenerTest() throws Exception {
Timing timing = new Timing();
ControlNodeInfo controlNodeInfo = buildControlNodeInfo();
ControlNodeInfo secondaryNodeInfo = buildSecondaryNodeInfo();
OperationsNodeInfo endpointNodeInfo = buildOperationsNodeInfo();
OperationsNode endpointNode = new OperationsNode(endpointNodeInfo, zkClient);
ControlNodeListener mockListener = mock(ControlNodeListener.class);
endpointNode.addListener(mockListener);
endpointNode.start();
ControlNode controlNode = new ControlNode(controlNodeInfo, zkClient);
controlNode.start();
ControlNode secondaryNode = new ControlNode(secondaryNodeInfo, zkClient);
secondaryNode.start();
timing.sleepABit();
verify(mockListener).onControlNodeChange(controlNodeInfo);
int random = new Random().nextInt();
controlNodeInfo.setBootstrapServerCount(random);
controlNode.updateNodeData(controlNodeInfo);
int random2 = new Random().nextInt();
secondaryNodeInfo.setBootstrapServerCount(random2);
secondaryNode.updateNodeData(secondaryNodeInfo);
timing.sleepABit();
verify(mockListener).onControlNodeChange(controlNodeInfo);
assertEquals(new Integer(random), endpointNode.getControlServerInfo().getBootstrapServerCount());
assertEquals(new Integer(random2), secondaryNode.getCurrentNodeInfo().getBootstrapServerCount());
controlNode.close();
timing.sleepABit();
verify(mockListener).onControlNodeDown();
verify(mockListener).onControlNodeChange(secondaryNodeInfo);
assertTrue(endpointNode.removeListener(mockListener));
assertFalse(endpointNode.removeListener(mockListener));
secondaryNode.close();
endpointNode.close();
}
private RetryPolicy buildDefaultRetryPolicy() {
return new ExponentialBackoffRetry(100, 1);
}
private OperationsNodeInfo buildOperationsNodeInfo() {
OperationsNodeInfo nodeInfo = new OperationsNodeInfo();
ByteBuffer testKeyData = ByteBuffer.wrap(new byte[]{10, 11, 12, 45, 34, 23, 67, 89, 66, 12});
nodeInfo.setConnectionInfo(new ConnectionInfo(ENDPOINT_NODE_HOST, 1000, testKeyData));
nodeInfo.setLoadInfo(new LoadInfo(1, 1.0));
nodeInfo.setTimeStarted(System.currentTimeMillis());
nodeInfo.setTransports(BootstrapNodeIT.getHttpAndTcpTransportMD());
return nodeInfo;
}
private ControlNodeInfo buildSecondaryNodeInfo() {
ControlNodeInfo secondaryNodeInfo = new ControlNodeInfo();
secondaryNodeInfo.setConnectionInfo(new ConnectionInfo(SECONDARY_NODE_HOST, 1000, null));
secondaryNodeInfo.setBootstrapServerCount(1);
secondaryNodeInfo.setOperationsServerCount(2);
return secondaryNodeInfo;
}
private ControlNodeInfo buildControlNodeInfo() {
ControlNodeInfo controlNodeInfo = new ControlNodeInfo();
controlNodeInfo.setConnectionInfo(new ConnectionInfo(CONTROL_NODE_HOST, 1000, null));
controlNodeInfo.setBootstrapServerCount(3);
controlNodeInfo.setOperationsServerCount(4);
return controlNodeInfo;
}
}