/*
* 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 gobblin.cluster;
import java.net.URL;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.test.TestingServer;
import org.apache.hadoop.fs.Path;
import org.apache.helix.HelixManager;
import org.apache.helix.HelixManagerFactory;
import org.apache.helix.InstanceType;
import org.apache.helix.model.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.io.Closer;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
import gobblin.cluster.event.ClusterManagerShutdownRequest;
import gobblin.testing.AssertWithBackoff;
/**
* Unit tests for {@link GobblinClusterManager}.
*
* <p>
* This class uses a {@link TestingServer} as an embedded ZooKeeper server for testing. The Curator
* framework is used to provide a ZooKeeper client. This class also uses the {@link HelixManager} to
* act as a testing Helix participant to receive the container (running the {@link GobblinTaskRunner})
* shutdown request message.
* </p>
*
* @author Yinan Li
*/
@Test(groups = { "gobblin.cluster" })
public class GobblinClusterManagerTest implements HelixMessageTestBase {
public final static Logger LOG = LoggerFactory.getLogger(GobblinClusterManagerTest.class);
private TestingServer testingZKServer;
private HelixManager helixManager;
private GobblinClusterManager gobblinClusterManager;
@BeforeClass
public void setUp() throws Exception {
// Use a random ZK port
this.testingZKServer = new TestingServer(-1);
LOG.info("Testing ZK Server listening on: " + testingZKServer.getConnectString());
URL url = GobblinClusterManagerTest.class.getClassLoader().getResource(
GobblinClusterManager.class.getSimpleName() + ".conf");
Assert.assertNotNull(url, "Could not find resource " + url);
Config config = ConfigFactory.parseURL(url)
.withValue("gobblin.cluster.zk.connection.string",
ConfigValueFactory.fromAnyRef(testingZKServer.getConnectString()))
.resolve();
String zkConnectionString = config.getString(GobblinClusterConfigurationKeys.ZK_CONNECTION_STRING_KEY);
HelixUtils.createGobblinHelixCluster(zkConnectionString,
config.getString(GobblinClusterConfigurationKeys.HELIX_CLUSTER_NAME_KEY));
this.helixManager = HelixManagerFactory
.getZKHelixManager(config.getString(GobblinClusterConfigurationKeys.HELIX_CLUSTER_NAME_KEY),
TestHelper.TEST_HELIX_INSTANCE_NAME, InstanceType.PARTICIPANT, zkConnectionString);
this.helixManager.connect();
this.helixManager.getMessagingService().registerMessageHandlerFactory(GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE,
new TestShutdownMessageHandlerFactory(this));
this.gobblinClusterManager =
new GobblinClusterManager(TestHelper.TEST_APPLICATION_NAME, TestHelper.TEST_APPLICATION_ID, config,
Optional.<Path>absent());
this.gobblinClusterManager.getEventBus().register(this.gobblinClusterManager);
this.gobblinClusterManager.connectHelixManager();
}
static class GetInstanceMessageNumFunc implements Function<Void, Integer> {
private final CuratorFramework curatorFramework;
private final String testName;
public GetInstanceMessageNumFunc(String testName, CuratorFramework curatorFramework) {
this.curatorFramework = curatorFramework;
this.testName = testName;
}
@Override
public Integer apply(Void input) {
try {
return this.curatorFramework.getChildren().forPath(String
.format("/%s/INSTANCES/%s/MESSAGES", this.testName,
TestHelper.TEST_HELIX_INSTANCE_NAME)).size();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testSendShutdownRequest() throws Exception {
Logger log = LoggerFactory.getLogger("testSendShutdownRequest");
Closer closer = Closer.create();
try {
CuratorFramework curatorFramework = TestHelper.createZkClient(this.testingZKServer, closer);
final GetInstanceMessageNumFunc getMessageNumFunc =
new GetInstanceMessageNumFunc(GobblinClusterManagerTest.class.getSimpleName(),
curatorFramework);
AssertWithBackoff assertWithBackoff =
AssertWithBackoff.create().logger(log).timeoutMs(30000);
this.gobblinClusterManager.sendShutdownRequest();
Assert.assertEquals(curatorFramework.checkExists().forPath(String
.format("/%s/INSTANCES/%s/MESSAGES", GobblinClusterManagerTest.class.getSimpleName(),
TestHelper.TEST_HELIX_INSTANCE_NAME)).getVersion(), 0);
assertWithBackoff.assertEquals(getMessageNumFunc, 1, "1 message queued");
// Give Helix sometime to handle the message
assertWithBackoff.assertEquals(getMessageNumFunc, 0, "all messages processed");
} finally {
closer.close();
}
}
@Test(dependsOnMethods = "testSendShutdownRequest")
public void testHandleClusterManagerShutdownRequest() throws Exception {
Logger log = LoggerFactory.getLogger("testHandleClusterManagerShutdownRequest");
this.gobblinClusterManager.getEventBus().post(new ClusterManagerShutdownRequest());
AssertWithBackoff.create().logger(log).timeoutMs(20000)
.assertTrue(new Predicate<Void>() {
@Override public boolean apply(Void input) {
return !GobblinClusterManagerTest.this.gobblinClusterManager.isHelixManagerConnected();
}
}, "Cluster Manager shutdown");
}
@AfterClass
public void tearDown() throws Exception {
try {
if (this.helixManager.isConnected()) {
this.helixManager.disconnect();
}
this.gobblinClusterManager.disconnectHelixManager();
} catch (Throwable t) {
Assert.fail();
} finally {
this.testingZKServer.close();
}
}
@Test(enabled = false)
@Override
public void assertMessageReception(Message message) {
Assert.assertEquals(message.getMsgType(), GobblinHelixConstants.SHUTDOWN_MESSAGE_TYPE);
Assert.assertEquals(message.getMsgSubType(), HelixMessageSubTypes.WORK_UNIT_RUNNER_SHUTDOWN.toString());
}
}