/**
* Copyright 2016 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.server;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.github.ambry.clustermap.MockClusterMap;
import com.github.ambry.commons.SSLFactory;
import com.github.ambry.commons.TestSSLUtils;
import com.github.ambry.server.RouterServerTestFramework.*;
import com.github.ambry.utils.SystemTime;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.Random;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static com.github.ambry.server.RouterServerTestFramework.*;
public class RouterServerSSLTest {
private static MockCluster sslCluster;
private static RouterServerTestFramework testFramework;
private static MetricRegistry routerMetricRegistry;
private static long sslSendBytesCountBeforeTest;
private static long sslReceiveBytesCountBeforeTest;
@BeforeClass
public static void initializeTests() throws Exception {
File trustStoreFile = File.createTempFile("truststore", ".jks");
String sslEnabledDataCentersStr = "DC1,DC2,DC3";
Properties serverSSLProps = new Properties();
TestSSLUtils.addSSLProperties(serverSSLProps, sslEnabledDataCentersStr, SSLFactory.Mode.SERVER, trustStoreFile,
"server");
Properties routerProps = getRouterProperties("DC1");
TestSSLUtils.addSSLProperties(routerProps, sslEnabledDataCentersStr, SSLFactory.Mode.CLIENT, trustStoreFile,
"router-client");
MockNotificationSystem notificationSystem = new MockNotificationSystem(9);
sslCluster = new MockCluster(notificationSystem, serverSSLProps, false, SystemTime.getInstance());
sslCluster.startServers();
MockClusterMap routerClusterMap = sslCluster.getClusterMap();
// MockClusterMap returns a new registry by default. This is to ensure that each node (server, router and so on,
// get a different registry. But at this point all server nodes have been initialized, and we want the router and
// its components, which are going to be created, to use the same registry.
routerClusterMap.createAndSetPermanentMetricRegistry();
testFramework = new RouterServerTestFramework(routerProps, routerClusterMap, notificationSystem);
routerMetricRegistry = routerClusterMap.getMetricRegistry();
}
@AfterClass
public static void cleanup() throws IOException {
testFramework.cleanup();
long start = System.currentTimeMillis();
System.out.println("About to invoke cluster.cleanup()");
if (sslCluster != null) {
sslCluster.cleanup();
}
System.out.println("cluster.cleanup() took " + (System.currentTimeMillis() - start) + " ms.");
}
@Before
public void before() {
Map<String, Meter> meters = routerMetricRegistry.getMeters();
sslSendBytesCountBeforeTest = meters.get(sslSendBytesMetricName).getCount();
sslReceiveBytesCountBeforeTest = meters.get(sslReceiveBytesMetricName).getCount();
}
@After
public void after() {
Map<String, Meter> meters = routerMetricRegistry.getMeters();
Assert.assertTrue("Router should have sent over SSL",
meters.get(sslSendBytesMetricName).getCount() != sslSendBytesCountBeforeTest);
Assert.assertTrue("Router should have received over SSL",
meters.get(sslReceiveBytesMetricName).getCount() != sslReceiveBytesCountBeforeTest);
Assert.assertTrue("Router should not have sent over Plain Text",
meters.get(plaintextSendBytesMetricName).getCount() == 0);
Assert.assertTrue("Router should not have received over Plain Text",
meters.get(plaintextReceiveBytesMetricName).getCount() == 0);
}
/**
* Test that the non blocking router can handle a large number of concurrent (small blob) operations without errors.
* This test creates chains of operations without waiting for previous operations to finish.
* @throws Exception
*/
@Test
public void interleavedOperationsTest() throws Exception {
List<OperationChain> opChains = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
Queue<OperationType> operations = new LinkedList<>();
switch (i % 3) {
case 0:
operations.add(OperationType.PUT);
operations.add(OperationType.AWAIT_CREATION);
operations.add(OperationType.GET);
operations.add(OperationType.GET_INFO);
operations.add(OperationType.DELETE);
operations.add(OperationType.AWAIT_DELETION);
operations.add(OperationType.GET_DELETED);
operations.add(OperationType.GET_INFO_DELETED);
operations.add(OperationType.GET_DELETED_SUCCESS);
operations.add(OperationType.GET_INFO_DELETED_SUCCESS);
break;
case 1:
operations.add(OperationType.PUT);
operations.add(OperationType.AWAIT_CREATION);
operations.add(OperationType.DELETE);
operations.add(OperationType.AWAIT_DELETION);
operations.add(OperationType.GET_DELETED);
operations.add(OperationType.GET_INFO_DELETED);
operations.add(OperationType.GET_DELETED);
operations.add(OperationType.GET_INFO_DELETED);
operations.add(OperationType.GET_DELETED_SUCCESS);
operations.add(OperationType.GET_INFO_DELETED_SUCCESS);
break;
case 2:
operations.add(OperationType.PUT);
operations.add(OperationType.AWAIT_CREATION);
operations.add(OperationType.GET);
operations.add(OperationType.GET);
operations.add(OperationType.GET);
operations.add(OperationType.GET_INFO);
break;
}
int blobSize = random.nextInt(100 * 1024);
opChains.add(testFramework.startOperationChain(blobSize, i, operations));
}
testFramework.checkOperationChains(opChains);
}
}