/*
* Copyright © 2014-2016 Cask Data, Inc.
*
* 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. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package co.cask.cdap.gateway.handlers.metrics;
import co.cask.cdap.api.metrics.MetricStore;
import co.cask.cdap.api.metrics.MetricsCollectionService;
import co.cask.cdap.app.metrics.MapReduceMetrics;
import co.cask.cdap.app.store.Store;
import co.cask.cdap.common.conf.CConfiguration;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.discovery.EndpointStrategy;
import co.cask.cdap.common.discovery.RandomEndpointStrategy;
import co.cask.cdap.common.guice.ConfigModule;
import co.cask.cdap.common.guice.DiscoveryRuntimeModule;
import co.cask.cdap.common.guice.LocationRuntimeModule;
import co.cask.cdap.data.runtime.DataFabricModules;
import co.cask.cdap.data.runtime.DataSetServiceModules;
import co.cask.cdap.data.runtime.DataSetsModules;
import co.cask.cdap.data2.datafabric.dataset.service.DatasetService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetOpExecutor;
import co.cask.cdap.explore.guice.ExploreClientModule;
import co.cask.cdap.gateway.handlers.log.MockLogReader;
import co.cask.cdap.internal.guice.AppFabricTestModule;
import co.cask.cdap.logging.read.LogReader;
import co.cask.cdap.metrics.guice.MetricsClientRuntimeModule;
import co.cask.cdap.metrics.guice.MetricsHandlerModule;
import co.cask.cdap.metrics.query.MetricsQueryService;
import co.cask.cdap.store.guice.NamespaceStoreModule;
import co.cask.tephra.TransactionManager;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.ObjectArrays;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
import com.google.inject.util.Modules;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.twill.discovery.DiscoveryServiceClient;
import org.apache.twill.filesystem.LocationFactory;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Provides base test class for MetricsServiceTestsSuite.
*/
public abstract class MetricsSuiteTestBase {
// Controls for test suite for whether to run BeforeClass/AfterClass
public static boolean runBefore = true;
public static boolean runAfter = true;
private static final String API_KEY = "SampleTestApiKey";
private static final String CLUSTER = "SampleTestClusterName";
private static final Header AUTH_HEADER = new BasicHeader(Constants.Gateway.API_KEY, API_KEY);
private static MetricsQueryService metrics;
private static final String hostname = "127.0.0.1";
private static int port;
private static File dataDir;
protected static MetricsCollectionService collectionService;
protected static Store store;
protected static LocationFactory locationFactory;
protected static List<String> nonExistingResources;
private static CConfiguration conf;
private static TemporaryFolder tmpFolder;
private static TransactionManager transactionManager;
private static DatasetOpExecutor dsOpService;
private static DatasetService datasetService;
protected static MetricStore metricStore;
protected static LogReader logReader;
private static Injector injector;
@BeforeClass
public static void beforeClass() throws Exception {
if (!runBefore) {
return;
}
tmpFolder = new TemporaryFolder();
conf = CConfiguration.create();
conf.set(Constants.Metrics.ADDRESS, hostname);
conf.set(Constants.AppFabric.OUTPUT_DIR, System.getProperty("java.io.tmpdir"));
conf.set(Constants.AppFabric.TEMP_DIR, System.getProperty("java.io.tmpdir"));
conf.setBoolean(Constants.Dangerous.UNRECOVERABLE_RESET, true);
conf.setBoolean(Constants.Metrics.CONFIG_AUTHENTICATION_REQUIRED, true);
conf.set(Constants.Metrics.CLUSTER_NAME, CLUSTER);
injector = startMetricsService(conf);
store = injector.getInstance(Store.class);
locationFactory = injector.getInstance(LocationFactory.class);
metricStore = injector.getInstance(MetricStore.class);
tmpFolder.create();
dataDir = tmpFolder.newFolder();
initialize();
}
@AfterClass
public static void afterClass() throws Exception {
if (!runAfter) {
return;
}
stopMetricsService(conf);
try {
stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
tmpFolder.delete();
}
}
@After
public void after() throws Exception {
metricStore.deleteAll();
}
public static void initialize() throws IOException {
CConfiguration cConf = CConfiguration.create();
// use this injector instead of the one in startMetricsService because that one uses a
// mock metrics collection service while we need a real one.
Injector injector = Guice.createInjector(Modules.override(
new ConfigModule(cConf),
new LocationRuntimeModule().getInMemoryModules(),
new DiscoveryRuntimeModule().getInMemoryModules(),
new MetricsHandlerModule(),
new MetricsClientRuntimeModule().getInMemoryModules(),
new DataFabricModules().getInMemoryModules(),
new DataSetsModules().getStandaloneModules(),
new DataSetServiceModules().getInMemoryModules(),
new ExploreClientModule(),
new NamespaceStoreModule().getInMemoryModules()
).with(new AbstractModule() {
@Override
protected void configure() {
bind(LogReader.class).to(MockLogReader.class).in(Scopes.SINGLETON);
}
}));
collectionService = injector.getInstance(MetricsCollectionService.class);
collectionService.startAndWait();
setupMeta();
}
public static void stop() {
collectionService.stopAndWait();
Deque<File> files = Lists.newLinkedList();
files.add(dataDir);
File file = files.peekLast();
while (file != null) {
File[] children = file.listFiles();
if (children == null || children.length == 0) {
files.pollLast().delete();
} else {
Collections.addAll(files, children);
}
file = files.peekLast();
}
}
public static Injector startMetricsService(CConfiguration conf) {
// Set up our Guice injections
injector = Guice.createInjector(Modules.override(
new AbstractModule() {
@Override
protected void configure() {
}
},
new AppFabricTestModule(conf)
).with(new AbstractModule() {
@Override
protected void configure() {
// It's a bit hacky to add it here. Need to refactor
// these bindings out as it overlaps with
// AppFabricServiceModule
bind(LogReader.class).to(MockLogReader.class).in(Scopes.SINGLETON);
}
}
));
transactionManager = injector.getInstance(TransactionManager.class);
transactionManager.startAndWait();
dsOpService = injector.getInstance(DatasetOpExecutor.class);
dsOpService.startAndWait();
datasetService = injector.getInstance(DatasetService.class);
datasetService.startAndWait();
metrics = injector.getInstance(MetricsQueryService.class);
metrics.startAndWait();
logReader = injector.getInstance(LogReader.class);
// initialize the dataset instantiator
DiscoveryServiceClient discoveryClient = injector.getInstance(DiscoveryServiceClient.class);
EndpointStrategy metricsEndPoints = new RandomEndpointStrategy(discoveryClient.discover(Constants.Service.METRICS));
port = metricsEndPoints.pick(1L, TimeUnit.SECONDS).getSocketAddress().getPort();
return injector;
}
public static void stopMetricsService(CConfiguration conf) {
datasetService.stopAndWait();
dsOpService.stopAndWait();
transactionManager.stopAndWait();
metrics.stopAndWait();
conf.clear();
}
// write WordCount app to metadata store
public static void setupMeta() {
nonExistingResources = ImmutableList.of(
"/system/apps/WordCont/reads?aggregate=true",
"/system/apps/WordCount/flows/WordCouner/reads?aggregate=true",
"/system/apps/WordCount/flows/WordCounter/flowlets/couter/reads?aggregate=true",
"/system/datasets/wordStat/reads?aggregate=true",
"/system/datasets/wordStat/apps/WordCount/reads?aggregate=true",
"/system/datasets/wordStas/apps/WordCount/flows/WordCounter/reads?aggregate=true",
"/system/datasets/wordStts/apps/WordCount/flows/WordCounter/flowlets/counter/reads?aggregate=true",
"/system/streams/wordStrea/collect.events?aggregate=true"
);
}
public static URI getEndPoint(String path) throws URISyntaxException {
return new URI("http://" + hostname + ":" + port + path);
}
public static HttpResponse doGet(String resource) throws Exception {
return doGet(resource, null);
}
public static HttpResponse doGet(String resource, Header[] headers) throws Exception {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(MetricsSuiteTestBase.getEndPoint(resource));
if (headers != null) {
get.setHeaders(ObjectArrays.concat(AUTH_HEADER, headers));
} else {
get.setHeader(AUTH_HEADER);
}
return client.execute(get);
}
public static HttpResponse doPost(String resource, String body) throws Exception {
return doPost(resource, body, null);
}
public static HttpResponse doPost(String resource, String body, Header[] headers) throws Exception {
DefaultHttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(MetricsSuiteTestBase.getEndPoint(resource));
if (body != null) {
post.setEntity(new StringEntity(body));
}
if (headers != null) {
post.setHeaders(ObjectArrays.concat(AUTH_HEADER, headers));
} else {
post.setHeader(AUTH_HEADER);
}
return client.execute(post);
}
/**
* Given a non-versioned API path, returns its corresponding versioned API path with v3 and default namespace.
*
* @param nonVersionedApiPath API path without version
* @param namespace the namespace
*/
public static String getVersionedAPIPath(String nonVersionedApiPath, String namespace) {
String version = Constants.Gateway.API_VERSION_3_TOKEN;
return String.format("/%s/namespaces/%s/%s", version, namespace, nonVersionedApiPath);
}
protected static Map<String, String> getFlowletContext(String namespaceId, String appName, String flowName,
String runId, String flowletName) {
return ImmutableMap.<String, String>builder()
.put(Constants.Metrics.Tag.NAMESPACE, namespaceId)
.put(Constants.Metrics.Tag.APP, appName)
.put(Constants.Metrics.Tag.FLOW, flowName)
.put(Constants.Metrics.Tag.RUN_ID, runId)
.put(Constants.Metrics.Tag.FLOWLET, flowletName).build();
}
protected static Map<String, String> getMapReduceTaskContext(String namespaceId, String appName, String jobName,
MapReduceMetrics.TaskType type,
String runId, String instanceId) {
return ImmutableMap.<String, String>builder()
.put(Constants.Metrics.Tag.NAMESPACE, namespaceId)
.put(Constants.Metrics.Tag.APP, appName)
.put(Constants.Metrics.Tag.MAPREDUCE, jobName)
.put(Constants.Metrics.Tag.MR_TASK_TYPE, type.getId())
.put(Constants.Metrics.Tag.RUN_ID, runId)
.put(Constants.Metrics.Tag.INSTANCE_ID, instanceId)
.build();
}
protected static Map<String, String> getWorkerContext(String namespaceId, String appName, String jobName,
String runId, String instanceId) {
return ImmutableMap.<String, String>builder()
.put(Constants.Metrics.Tag.NAMESPACE, namespaceId)
.put(Constants.Metrics.Tag.APP, appName)
.put(Constants.Metrics.Tag.WORKER, jobName)
.put(Constants.Metrics.Tag.RUN_ID, runId)
.put(Constants.Metrics.Tag.INSTANCE_ID, instanceId)
.build();
}
}