/** * 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.agent; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.avro.ipc.AvroRemoteException; import org.apache.avro.ipc.HttpServer; import org.apache.avro.ipc.Server; import org.apache.avro.specific.SpecificResponder; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransportException; import org.mortbay.log.Log; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudera.flume.conf.FlumeConfigData; import com.cloudera.flume.conf.FlumeConfiguration; import com.cloudera.flume.conf.avro.AvroFlumeConfigData; import com.cloudera.flume.reporter.server.avro.AvroFlumeReport; import com.cloudera.flume.conf.avro.AvroFlumeClientServer; import com.cloudera.flume.conf.avro.FlumeNodeState; import com.cloudera.flume.conf.thrift.ThriftFlumeClientServer; import com.cloudera.flume.conf.thrift.ThriftFlumeConfigData; import com.cloudera.flume.conf.thrift.ThriftFlumeClientServer.Iface; import com.cloudera.flume.master.MasterClientServerAvro; import com.cloudera.flume.master.MasterClientServerThrift; import com.cloudera.flume.master.StatusManager; import com.cloudera.flume.reporter.ReportEvent; import com.cloudera.flume.reporter.server.thrift.ThriftFlumeReport; import com.cloudera.flume.util.ThriftServer; /** * Test that both Thrift and Avro RPC type translation is working. Also tests * basic stub client/server interaction. */ public class TestRPCMechanisms { static final Logger LOG = LoggerFactory.getLogger(TestRPCMechanisms.class); /** * Mock AvroServer. */ public class MockAvroServer implements AvroFlumeClientServer { protected Server server; public MockAvroServer() { } public void stop() { this.server.close(); } public void serve(int port) throws IOException { LOG .info(String .format( "Starting blocking thread pool server for control server on port %d...", port)); SpecificResponder res = new SpecificResponder( AvroFlumeClientServer.class, this); this.server = new HttpServer(res, port); this.server.start(); } @Override public java.lang.Void acknowledge(CharSequence ackid) throws AvroRemoteException { return null; } @Override public boolean checkAck(CharSequence ackid) throws AvroRemoteException { Log.info("checkAck called at server on " + this.server.getPort()); return true; } @Override public AvroFlumeConfigData getConfig(CharSequence sourceId) throws AvroRemoteException { Log.info("getConfig called at server on " + this.server.getPort()); FlumeConfigData out = new FlumeConfigData(); out.flowID = "flowID"; out.sinkConfig = "sinkConfig"; out.sinkVersion = 112233; out.sourceConfig = "sourceConfig"; out.sourceVersion = 445566; out.timestamp = 78901; return MasterClientServerAvro.configToAvro(out); } @Override public List<CharSequence> getLogicalNodes(CharSequence physNode) throws AvroRemoteException { Log.info("getLogicalNodes called at server on " + this.server.getPort()); List<CharSequence> out = new ArrayList<CharSequence>(); out.add("one"); out.add("two"); out.add("three"); out.add("four"); return out; } @Override public boolean heartbeat(CharSequence logicalNode, CharSequence physicalNode, CharSequence clienthost, FlumeNodeState s, long timestamp) throws AvroRemoteException { Log.info("heartbeat called at server on " + this.server.getPort()); return true; } @Override public Void putReports(Map<CharSequence, AvroFlumeReport> reports) throws AvroRemoteException { Log.info("putReports called at server on " + this.server.getPort()); assertEquals(1, reports.size()); AvroFlumeReport report = reports.get("reportKey"); assertNotNull(report); Map<CharSequence, Long> longMetrics = report.longMetrics; assertEquals(2, longMetrics.size()); assertEquals(Long.MIN_VALUE, ((Long) longMetrics.get("long1")) .longValue()); assertEquals(Long.MAX_VALUE, ((Long) longMetrics.get("long2")) .longValue()); Map<CharSequence, Double> doubleMetrics = report.doubleMetrics; assertEquals(2, doubleMetrics.size()); assertEquals(Double.MIN_VALUE, ((Double) doubleMetrics.get("double1")) .doubleValue(), 0.0001); assertEquals(Double.MAX_VALUE, ((Double) doubleMetrics.get("double2")) .doubleValue(), 0.0001); Map<CharSequence, CharSequence> stringMetrics = report.stringMetrics; assertEquals(2, stringMetrics.size()); assertEquals("string1", stringMetrics.get("string1").toString()); assertEquals("string2", stringMetrics.get("string2").toString()); return null; } @Override public Map<CharSequence, Integer> getChokeMap(CharSequence physNode) throws AvroRemoteException { return null; } } /** * Mock ThriftServer. */ public class MockThriftServer extends ThriftServer implements ThriftFlumeClientServer.Iface { @Override public void acknowledge(String ackid) throws TException { } @Override public boolean checkAck(String ackid) throws TException { Log.info("checkAck called at server on " + this.port); return true; } @Override public ThriftFlumeConfigData getConfig(String sourceId) throws TException { Log.info("getConfig called at server on " + this.port); FlumeConfigData out = new FlumeConfigData(); out.flowID = "flowID"; out.sinkConfig = "sinkConfig"; out.sinkVersion = 112233; out.sourceConfig = "sourceConfig"; out.sourceVersion = 445566; out.timestamp = 78901; return MasterClientServerThrift.configToThrift(out); } @Override public List<String> getLogicalNodes(String physNode) throws TException { Log.info("getLogicalNodes called at server on " + this.port); List<String> out = new LinkedList<String>(); out.add("one"); out.add("two"); out.add("three"); out.add("four"); return out; } @Override public boolean heartbeat(String logicalNode, String physicalNode, String host, com.cloudera.flume.conf.thrift.FlumeNodeState s, long timestamp) throws TException { Log.info("heartbeat called at server on " + this.port); return true; } @Override public void putReports(Map<String, ThriftFlumeReport> reports) throws TException { Log.info("putReports called at server on " + this.port); assertEquals(1, reports.size()); ThriftFlumeReport report = reports.get("reportKey"); assertNotNull(report); Map<String, Long> longMetrics = report.longMetrics; assertEquals(2, longMetrics.size()); assertEquals(Long.MIN_VALUE, ((Long) longMetrics.get("long1")) .longValue()); assertEquals(Long.MAX_VALUE, ((Long) longMetrics.get("long2")) .longValue()); Map<String, Double> doubleMetrics = report.doubleMetrics; assertEquals(2, doubleMetrics.size()); assertEquals(Double.MIN_VALUE, ((Double) doubleMetrics.get("double1")) .doubleValue(), .0001); assertEquals(Double.MAX_VALUE, ((Double) doubleMetrics.get("double2")) .doubleValue(), .0001); Map<String, String> stringMetrics = report.stringMetrics; assertEquals(2, stringMetrics.size()); assertEquals("string1", stringMetrics.get("string1").toString()); assertEquals("string2", stringMetrics.get("string2").toString()); } public void setPort(int port) { this.port = port; } public void serve() throws IOException { LOG .info(String .format( "Starting blocking thread pool server for control server on port %d...", port)); try { this.start(new ThriftFlumeClientServer.Processor((Iface) this), port, "MasterClientServer"); } catch (TTransportException e) { throw new IOException(e.getMessage()); } } @Override public Map<String, Integer> getChokeMap(String physNode) throws TException { return null; } } /** * Connect to stub server and make sure types are converted correctly. * * We test both Avro and Thrift version of the client/server interaction. * * @throws IOException */ @Test public void testConnect() throws IOException { FlumeConfiguration conf = FlumeConfiguration.get(); conf.set(FlumeConfiguration.MASTER_HEARTBEAT_SERVERS, "localhost:44444"); conf.set(FlumeConfiguration.MASTER_HEARBEAT_RPC, "AVRO"); MultiMasterRPC masterRPC = new MultiMasterRPC(conf, false); MockAvroServer server1 = new MockAvroServer(); server1.serve(44444); runTest(masterRPC); server1.stop(); conf.set(FlumeConfiguration.MASTER_HEARBEAT_RPC, "THRIFT"); masterRPC = new MultiMasterRPC(conf, false); MockThriftServer server2 = new MockThriftServer(); server2.setPort(44444); server2.serve(); runTest(masterRPC); } public void runTest(MultiMasterRPC masterRPC) throws IOException { assertEquals(true, masterRPC.checkAck("FOO")); List<String> logicalNodes = masterRPC.getLogicalNodes("Unused"); assertEquals(4, logicalNodes.size()); assertEquals("one", logicalNodes.get(0)); assertEquals("two", logicalNodes.get(1)); assertEquals("three", logicalNodes.get(2)); assertEquals("four", logicalNodes.get(3)); FlumeConfigData data = masterRPC.getConfig(new LogicalNode(null, "")); assertEquals("flowID", data.flowID); assertEquals("sinkConfig", data.sinkConfig); assertEquals(112233, data.sinkVersion); assertEquals("sourceConfig", data.sourceConfig); assertEquals(445566, data.sourceVersion); assertEquals(78901, data.timestamp); Map<String, Long> longMetrics = new HashMap<String, Long>(); Map<String, String> stringMetrics = new HashMap<String, String>(); Map<String, Double> doubleMetrics = new HashMap<String, Double>(); longMetrics.put("long1", (long) Long.MIN_VALUE); longMetrics.put("long2", (long) Long.MAX_VALUE); stringMetrics.put("string1", "string1"); stringMetrics.put("string2", "string2"); doubleMetrics.put("double1", Double.MIN_VALUE); doubleMetrics.put("double2", Double.MAX_VALUE); Map<String, ReportEvent> reports = new HashMap<String, ReportEvent>(); reports.put("reportKey", new ReportEvent(longMetrics, stringMetrics, doubleMetrics)); masterRPC.putReports(reports); } @Test public void testTypeConversions() { // THRIFT NODE STATE assertEquals(StatusManager.NodeState.HELLO, MasterClientServerThrift .stateFromThrift(com.cloudera.flume.conf.thrift.FlumeNodeState.HELLO)); assertEquals(StatusManager.NodeState.ACTIVE, MasterClientServerThrift .stateFromThrift(com.cloudera.flume.conf.thrift.FlumeNodeState.ACTIVE)); assertEquals(StatusManager.NodeState.ERROR, MasterClientServerThrift .stateFromThrift(com.cloudera.flume.conf.thrift.FlumeNodeState.ERROR)); assertEquals(StatusManager.NodeState.IDLE, MasterClientServerThrift .stateFromThrift(com.cloudera.flume.conf.thrift.FlumeNodeState.IDLE)); assertEquals( StatusManager.NodeState.CONFIGURING, MasterClientServerThrift .stateFromThrift(com.cloudera.flume.conf.thrift.FlumeNodeState.CONFIGURING)); assertEquals(com.cloudera.flume.conf.thrift.FlumeNodeState.HELLO, MasterClientServerThrift.stateToThrift(StatusManager.NodeState.HELLO)); assertEquals(com.cloudera.flume.conf.thrift.FlumeNodeState.ACTIVE, MasterClientServerThrift.stateToThrift(StatusManager.NodeState.ACTIVE)); assertEquals(com.cloudera.flume.conf.thrift.FlumeNodeState.ERROR, MasterClientServerThrift.stateToThrift(StatusManager.NodeState.ERROR)); assertEquals(com.cloudera.flume.conf.thrift.FlumeNodeState.IDLE, MasterClientServerThrift.stateToThrift(StatusManager.NodeState.IDLE)); assertEquals(com.cloudera.flume.conf.thrift.FlumeNodeState.CONFIGURING, MasterClientServerThrift .stateToThrift(StatusManager.NodeState.CONFIGURING)); // AVRO NODE STATE assertEquals(StatusManager.NodeState.HELLO, MasterClientServerAvro .stateFromAvro(FlumeNodeState.HELLO)); assertEquals(StatusManager.NodeState.ACTIVE, MasterClientServerAvro .stateFromAvro(FlumeNodeState.ACTIVE)); assertEquals(StatusManager.NodeState.ERROR, MasterClientServerAvro .stateFromAvro(FlumeNodeState.ERROR)); assertEquals(StatusManager.NodeState.IDLE, MasterClientServerAvro .stateFromAvro(FlumeNodeState.IDLE)); assertEquals(StatusManager.NodeState.CONFIGURING, MasterClientServerAvro .stateFromAvro(FlumeNodeState.CONFIGURING)); assertEquals(FlumeNodeState.HELLO, MasterClientServerAvro .stateToAvro(StatusManager.NodeState.HELLO)); assertEquals(FlumeNodeState.ACTIVE, MasterClientServerAvro .stateToAvro(StatusManager.NodeState.ACTIVE)); assertEquals(FlumeNodeState.ERROR, MasterClientServerAvro .stateToAvro(StatusManager.NodeState.ERROR)); assertEquals(FlumeNodeState.IDLE, MasterClientServerAvro .stateToAvro(StatusManager.NodeState.IDLE)); assertEquals(FlumeNodeState.CONFIGURING, MasterClientServerAvro .stateToAvro(StatusManager.NodeState.CONFIGURING)); } }