/*
* Copyright 2017 LinkedIn Corp. All rights reserved.
*
* 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.
*/
package com.github.ambry.clustermap;
import com.github.ambry.config.ClusterMapConfig;
import com.github.ambry.config.VerifiableProperties;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.helix.ClusterMessagingService;
import org.apache.helix.ConfigAccessor;
import org.apache.helix.ConfigChangeListener;
import org.apache.helix.ControllerChangeListener;
import org.apache.helix.CurrentStateChangeListener;
import org.apache.helix.ExternalViewChangeListener;
import org.apache.helix.HelixAdmin;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.HelixManager;
import org.apache.helix.HelixManagerProperties;
import org.apache.helix.IdealStateChangeListener;
import org.apache.helix.InstanceConfigChangeListener;
import org.apache.helix.InstanceType;
import org.apache.helix.LiveInstanceChangeListener;
import org.apache.helix.LiveInstanceInfoProvider;
import org.apache.helix.MessageListener;
import org.apache.helix.NotificationContext;
import org.apache.helix.PreConnectCallback;
import org.apache.helix.PropertyKey;
import org.apache.helix.ScopedConfigChangeListener;
import org.apache.helix.ZNRecord;
import org.apache.helix.healthcheck.ParticipantHealthReportCollector;
import org.apache.helix.messaging.handling.MessageHandler;
import org.apache.helix.model.HelixConfigScope;
import org.apache.helix.model.LeaderStandbySMD;
import org.apache.helix.model.Message;
import org.apache.helix.participant.StateMachineEngine;
import org.apache.helix.participant.statemachine.StateModel;
import org.apache.helix.participant.statemachine.StateModelFactory;
import org.apache.helix.store.zk.ZkHelixPropertyStore;
import org.json.JSONObject;
import org.junit.Test;
import static com.github.ambry.clustermap.TestUtils.*;
import static org.junit.Assert.*;
/**
* Test for {@link HelixParticipant}
*/
public class HelixParticipantTest {
private final MockHelixManagerFactory helixManagerFactory;
private final Properties props;
public HelixParticipantTest() throws Exception {
List<ZkInfo> zkInfoList = new ArrayList<>();
zkInfoList.add(new ZkInfo(null, "DC0", 2199, false));
JSONObject zkJson = constructZkLayoutJSON(zkInfoList);
props = new Properties();
props.setProperty("clustermap.host.name", "localhost");
props.setProperty("clustermap.cluster.name", "HelixParticipantTestCluster");
props.setProperty("clustermap.datacenter.name", "DC0");
props.setProperty("clustermap.dcs.zk.connect.strings", zkJson.toString(2));
helixManagerFactory = new MockHelixManagerFactory();
}
/**
* Test bad instantiation and initialization scenarios of the {@link HelixParticipant}
* @throws IOException
*/
@Test
public void testBadCases() throws IOException {
// Connect failure.
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
helixManagerFactory.helixManager.beBad = true;
HelixParticipant helixParticipant = new HelixParticipant(clusterMapConfig, helixManagerFactory);
try {
helixParticipant.initialize("localhost", 2200);
fail("Initialization should have failed");
} catch (IOException e) {
// OK
}
// Bad param during instantiation.
props.setProperty("clustermap.cluster.name", "");
clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
try {
new HelixParticipant(clusterMapConfig, helixManagerFactory);
fail("Instantiation should have failed");
} catch (IllegalStateException e) {
// OK
}
props.setProperty("clustermap.cluster.name", "HelixParticipantTestCluster");
props.setProperty("clustermap.dcs.zk.connect.strings", "");
clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
try {
new HelixParticipant(clusterMapConfig, helixManagerFactory);
fail("Instantiation should have failed");
} catch (IOException e) {
// OK
}
}
/**
* Test the good path of instantiation, initialization and termination of the {@link HelixParticipant}
* @throws Exception
*/
@Test
public void testHelixParticipant() throws Exception {
ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
HelixParticipant participant = new HelixParticipant(clusterMapConfig, helixManagerFactory);
participant.initialize("localhost", 2200);
MockHelixManager helixManager = helixManagerFactory.helixManager;
assertTrue(helixManager.isConnected());
assertEquals(LeaderStandbySMD.name, helixManager.stateModelDef);
assertEquals(AmbryStateModelFactory.class, helixManager.stateModelFactory.getClass());
participant.close();
assertFalse(helixManager.isConnected());
}
/**
* A Mock implementaion of {@link HelixFactory} that returns the {@link MockHelixManager}
*/
private static class MockHelixManagerFactory extends HelixFactory {
private final MockHelixManager helixManager;
/**
* Construct this factory.
*/
MockHelixManagerFactory() {
helixManager = new MockHelixManager();
}
/**
* Return the {@link MockHelixManager}
* @param clusterName unused.
* @param instanceName unused.
* @param instanceType unused.
* @param zkAddr unused.
* @return the {@link MockHelixManager}
*/
HelixManager getZKHelixManager(String clusterName, String instanceName, InstanceType instanceType, String zkAddr) {
return helixManager;
}
}
/**
* A mock implementation of the {@link HelixManager} for exclusive use for testing the {@link HelixParticipant}
*/
private static class MockHelixManager implements HelixManager {
private String stateModelDef;
private StateModelFactory stateModelFactory;
private boolean isConnected;
boolean beBad;
@Override
public StateMachineEngine getStateMachineEngine() {
return new StateMachineEngine() {
@Override
public boolean registerStateModelFactory(String stateModelDef,
StateModelFactory<? extends StateModel> factory) {
MockHelixManager.this.stateModelDef = stateModelDef;
stateModelFactory = factory;
return true;
}
@Override
public boolean registerStateModelFactory(String stateModelDef, StateModelFactory<? extends StateModel> factory,
String factoryName) {
throw new IllegalStateException("Not implemented");
}
@Override
public boolean removeStateModelFactory(String stateModelDef, StateModelFactory<? extends StateModel> factory) {
return false;
}
@Override
public boolean removeStateModelFactory(String stateModelDef, StateModelFactory<? extends StateModel> factory,
String factoryName) {
return false;
}
@Override
public MessageHandler createHandler(Message message, NotificationContext context) {
return null;
}
@Override
public String getMessageType() {
return null;
}
@Override
public void reset() {
}
};
}
@Override
public void connect() throws Exception {
if (beBad) {
throw new IOException("Being bad");
}
isConnected = true;
}
@Override
public boolean isConnected() {
return isConnected;
}
@Override
public void disconnect() {
isConnected = false;
}
//****************************
// Not implemented.
//****************************
@Override
public void addIdealStateChangeListener(IdealStateChangeListener listener) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addLiveInstanceChangeListener(LiveInstanceChangeListener listener) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addConfigChangeListener(ConfigChangeListener listener) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addInstanceConfigChangeListener(InstanceConfigChangeListener listener) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addConfigChangeListener(ScopedConfigChangeListener listener, HelixConfigScope.ConfigScopeProperty scope)
throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addMessageListener(MessageListener listener, String instanceName) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addCurrentStateChangeListener(CurrentStateChangeListener listener, String instanceName,
String sessionId) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addExternalViewChangeListener(ExternalViewChangeListener listener) throws Exception {
throw new IllegalStateException("Not implemented");
}
@Override
public void addControllerListener(ControllerChangeListener listener) {
throw new IllegalStateException("Not implemented");
}
@Override
public void addControllerMessageListener(MessageListener listener) {
throw new IllegalStateException("Not implemented");
}
@Override
public boolean removeListener(PropertyKey key, Object listener) {
throw new IllegalStateException("Not implemented");
}
@Override
public HelixDataAccessor getHelixDataAccessor() {
throw new IllegalStateException("Not implemented");
}
@Override
public ConfigAccessor getConfigAccessor() {
throw new IllegalStateException("Not implemented");
}
@Override
public String getClusterName() {
throw new IllegalStateException("Not implemented");
}
@Override
public String getInstanceName() {
throw new IllegalStateException("Not implemented");
}
@Override
public String getSessionId() {
throw new IllegalStateException("Not implemented");
}
@Override
public long getLastNotificationTime() {
throw new IllegalStateException("Not implemented");
}
@Override
public HelixAdmin getClusterManagmentTool() {
throw new IllegalStateException("Not implemented");
}
@Override
public ZkHelixPropertyStore<ZNRecord> getHelixPropertyStore() {
throw new IllegalStateException("Not implemented");
}
@Override
public ClusterMessagingService getMessagingService() {
throw new IllegalStateException("Not implemented");
}
@Override
public InstanceType getInstanceType() {
throw new IllegalStateException("Not implemented");
}
@Override
public String getVersion() {
throw new IllegalStateException("Not implemented");
}
@Override
public HelixManagerProperties getProperties() {
throw new IllegalStateException("Not implemented");
}
@Override
public boolean isLeader() {
throw new IllegalStateException("Not implemented");
}
@Override
public void startTimerTasks() {
throw new IllegalStateException("Not implemented");
}
@Override
public void stopTimerTasks() {
throw new IllegalStateException("Not implemented");
}
@Override
public void addPreConnectCallback(PreConnectCallback callback) {
throw new IllegalStateException("Not implemented");
}
@Override
public void setLiveInstanceInfoProvider(LiveInstanceInfoProvider liveInstanceInfoProvider) {
throw new IllegalStateException("Not implemented");
}
@Override
public ParticipantHealthReportCollector getHealthReportCollector() {
throw new IllegalStateException("Not implemented");
}
}
}