/** * Licensed to Cloudera, Inc. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.flume.master; 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 java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import org.junit.Before; import org.junit.Test; import com.cloudera.flume.conf.FlumeConfiguration; import com.cloudera.util.Clock; /** * The command manager needs to be able open and close with sane semantics. */ public class TestCommandManager { public static Logger LOG = Logger.getLogger(TestCommandManager.class); @Before public void setCfg() { // Isolate tests by only using simple cfg store FlumeConfiguration.get().set(FlumeConfiguration.MASTER_STORE, "memory"); } /** * Tests to make sure stop is blocks until thread is essentially done and that * subsequent starts don't get into an odd state. If there should be no * exceptions thrown -- this indicates problems in the shutdown or startup. * * There are no guarentees that all of the entries have executed. */ public void testStartStop() { CommandManager cmdman = new CommandManager(); for (int i = 0; i < 10; i++) { LOG.info("command manager start stop " + i); cmdman.start(); Command cmd = new Command("noop"); cmdman.submit(cmd); cmdman.stop(); } } /** * We allow only one thread; if multiple starts then ignore second */ @Test public void testStartStartStop() { CommandManager cmdman = new CommandManager(); cmdman.start(); cmdman.start(); cmdman.stop(); } /** * This submits 10 noop commands to the command manager. */ @Test public void testCommandManager() throws InterruptedException { CommandManager cmdman = new CommandManager(); List<Long> ids = new ArrayList<Long>(); for (int i = 0; i < 10; i++) { LOG.info("command manager start stop " + i); Command cmd = new Command("noop"); long id = cmdman.submit(cmd); ids.add(id); assertFalse(cmdman.isFailure(id)); assertFalse(cmdman.isSuccess(id)); } // all commands should be queued for (Long l : ids) { CommandStatus status = cmdman.getStatus(l); assertEquals("", status.getMessage()); assertTrue(status.isQueued()); } // this should be plenty of time to execute noops cmdman.start(); Clock.sleep(200); cmdman.stop(); // all commands should have executed successfully by now. for (Long l : ids) { CommandStatus status = cmdman.getStatus(l); assertEquals("", status.getMessage()); assertTrue(status.isSuccess()); assertFalse(cmdman.isFailure(l)); assertTrue(cmdman.isSuccess(l)); } LOG.info(cmdman.getReport()); } /** * This is normally only used by the JSP forms to fill in values. */ @Test public void testMultiConfigForm() { MultiConfigCommand mcc = new MultiConfigCommand(); String spec = "node: null | null;"; mcc.setSpecification(spec); Command c = mcc.toCommand(); assertEquals("multiconfig", c.getCommand()); assertEquals(spec, c.getArgs()[0]); } /** * JSP config form */ @Test public void testConfigForm() { ConfigCommand cc = new ConfigCommand(); cc.setNode("node"); cc.setSource("src"); cc.setSink("sink"); Command c = cc.toCommand(); assertEquals("config", c.getCommand()); assertEquals("node", c.getArgs()[0]); assertEquals("src", c.getArgs()[1]); assertEquals("sink", c.getArgs()[2]); } @Test public void testConfigFormChoice() { ConfigCommand cc = new ConfigCommand(); cc.setNodeChoice("chosen"); cc.setSource("src"); cc.setSink("sink"); Command c = cc.toCommand(); assertEquals("config", c.getCommand()); assertEquals("chosen", c.getArgs()[0]); assertEquals("src", c.getArgs()[1]); assertEquals("sink", c.getArgs()[2]); } @Test public void testConfigFormChoiceLoses() { ConfigCommand cc = new ConfigCommand(); cc.setNodeChoice("loser"); cc.setNode("chosen"); cc.setSource("src"); cc.setSink("sink"); Command c = cc.toCommand(); assertEquals("config", c.getCommand()); assertEquals("chosen", c.getArgs()[0]); assertEquals("src", c.getArgs()[1]); assertEquals("sink", c.getArgs()[2]); } @Test(expected = NullPointerException.class) public void testSubmitNull() throws InterruptedException { CommandManager cmdman = new CommandManager(); cmdman.submit(null); } @Test(expected = NullPointerException.class) public void testExecNull() throws MasterExecException { CommandManager cmdman = new CommandManager(); cmdman.exec(null); } @Test(expected = NullPointerException.class) public void testExecCmdNull() throws MasterExecException { CommandManager cmdman = new CommandManager(); cmdman.exec(new Command(null)); } @Test(expected = MasterExecException.class) public void testExecInvalidCmd() throws MasterExecException { CommandManager cmdman = new CommandManager(); cmdman.exec(new Command("illegal")); } @Test(expected = MasterExecException.class) public void testExecIOE() throws MasterExecException { Execable ex = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { throw new IOException(""); } }; CommandManager cmdman = new CommandManager(); cmdman.addCommand("exe", ex); cmdman.exec(new Command("exe")); } @Test(expected = IllegalArgumentException.class) public void testExecIAE() throws MasterExecException { Execable ex = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { throw new IllegalArgumentException(""); } }; CommandManager cmdman = new CommandManager(); cmdman.addCommand("exe", ex); cmdman.exec(new Command("exe")); } @Test public void testAddOverride() throws MasterExecException { final AtomicInteger i = new AtomicInteger(0); Execable ex = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { i.set(1); } }; Execable ex2 = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { i.set(2); } }; CommandManager cmdman = new CommandManager(); cmdman.addCommand("exe", ex); cmdman.addCommand("exe", ex2); cmdman.exec(new Command("exe")); assertEquals(2, i.get()); } /** * With no valid command in history, both should return false. */ @Test public void testCheckInvalidID() { CommandManager cmdman = new CommandManager(); assertFalse(cmdman.isFailure(0)); assertFalse(cmdman.isSuccess(0)); } @Test public void testExecFailure() throws MasterExecException, InterruptedException { Execable ex = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { throw new IllegalArgumentException("failure here"); } }; CommandManager cmdman = new CommandManager(); cmdman.addCommand("exe", ex); long id = cmdman.submit(new Command("exe")); cmdman.start(); Clock.sleep(100); cmdman.stop(); assertTrue(cmdman.isFailure(id)); } @Test public void testHandleCommand() { Execable ex = new Execable() { @Override public void exec(String[] args) throws MasterExecException, IOException { throw new IllegalArgumentException("failure here"); } }; CommandManager cmdman = new CommandManager(); cmdman.addCommand("err", ex); cmdman.handleCommand(null); // do nothing. // submit a error command long id = cmdman.submit(new Command("err")); CommandStatus stat = cmdman.getStatus(id); cmdman.handleCommand(stat); assertTrue(cmdman.isFailure(id)); id = cmdman.submit(new Command("noop")); stat = cmdman.getStatus(id); cmdman.handleCommand(stat); assertTrue(cmdman.isSuccess(id)); } /** * Uninteresting test, just to get coverage. */ public void testName() { CommandManager cmdman = new CommandManager(); cmdman.getName(); } @Test public void testSubmitConfigCommand() throws InterruptedException { CommandManager cmdman = new CommandManager(); cmdman.start(); // send valid commands with good and bad configs // Note - this calls into FlumeMaster.getInstance, which should normally // require it to be started. We set the cfgStore to "memory" so we don't // have to bother with serve and stop long good = cmdman.submit(new Command("config", "node", "null", "null")); long bad = cmdman.submit(new Command("config", "node", "fargle", "foogle")); Clock.sleep(1000); cmdman.stop(); assertTrue(cmdman.isSuccess(good)); assertTrue(cmdman.isFailure(bad)); } @Test public void testSubmitMultiConfigCommand() throws InterruptedException { CommandManager cmdman = new CommandManager(); cmdman.start(); // send valid commands with good and bad configs long good = cmdman.submit(new Command("multiconfig", "node1 : null | null; node2: null | null;")); long badSyntax = cmdman.submit(new Command("multiconfig", "node3 : null | null; node4: null| null")); // forgot semi long badVals = cmdman.submit(new Command("multiconfig", "node5f : null | null; node6: foogle| bargle;")); // TODO (jon) add signal so that this command returns when command is // resolved to be error or success Clock.sleep(10000); cmdman.stop(); assertTrue(cmdman.isSuccess(good)); assertTrue(cmdman.isFailure(badSyntax)); assertTrue(cmdman.isFailure(badVals)); assertNotNull(FlumeMaster.getInstance().getSpecMan().getConfig("node1")); assertNotNull(FlumeMaster.getInstance().getSpecMan().getConfig("node2")); assertNull(FlumeMaster.getInstance().getSpecMan().getConfig("node3")); assertNull(FlumeMaster.getInstance().getSpecMan().getConfig("node4")); assertNull(FlumeMaster.getInstance().getSpecMan().getConfig("node5")); assertNull(FlumeMaster.getInstance().getSpecMan().getConfig("node6")); } @Test public void testUnmapAllCommand() throws InterruptedException { CommandManager cmdman = new CommandManager(); cmdman.start(); // send valid commands with good and bad configs long good = cmdman.submit(new Command("multiconfig", "node1 : null | null; node2: null | null;")); long logical1 = cmdman.submit(new Command("spawn", "physical", "node1")); long logical2 = cmdman.submit(new Command("spawn", "physical", "node2")); long unmap = cmdman.submit(new Command("unmapAll")); // TODO (jon) add signal so that this command returns when command is // resolved to be error or success Clock.sleep(10000); assertTrue(cmdman.isSuccess(good)); assertTrue(cmdman.isSuccess(logical1)); assertTrue(cmdman.isSuccess(logical2)); assertTrue(cmdman.isSuccess(unmap)); ConfigurationManager cfg = FlumeMaster.getInstance().getSpecMan(); assertNotNull(cfg.getConfig("node1")); assertNotNull(cfg.getConfig("node2")); assertFalse(cfg.getLogicalNode("physical").contains("node1")); assertFalse(cfg.getLogicalNode("physical").contains("node2")); } @Test public void testNoopSleep() throws InterruptedException { CommandManager cmdman = new CommandManager(); cmdman.start(); long start = Clock.unixTime(); long good = cmdman.submit(new Command("noop", "5000")); CommandStatus stat = null; do { Clock.sleep(100); stat = cmdman.getStatus(good); } while (stat.isInProgress()); assertTrue(cmdman.isSuccess(good)); assertTrue(Clock.unixTime() - start >= 5000); } }