/*
* Copyright © 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.admin;
import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.api.dataset.lib.FileSet;
import co.cask.cdap.api.dataset.lib.FileSetProperties;
import co.cask.cdap.api.dataset.lib.KeyValueTable;
import co.cask.cdap.api.dataset.table.Get;
import co.cask.cdap.api.dataset.table.Put;
import co.cask.cdap.api.dataset.table.Table;
import co.cask.cdap.internal.guava.reflect.TypeToken;
import co.cask.cdap.test.ApplicationManager;
import co.cask.cdap.test.DataSetManager;
import co.cask.cdap.test.FlowManager;
import co.cask.cdap.test.MapReduceManager;
import co.cask.cdap.test.ProgramManager;
import co.cask.cdap.test.ServiceManager;
import co.cask.cdap.test.SparkManager;
import co.cask.cdap.test.StreamManager;
import co.cask.cdap.test.TestConfiguration;
import co.cask.cdap.test.WorkerManager;
import co.cask.cdap.test.WorkflowManager;
import co.cask.cdap.test.base.TestFrameworkTestBase;
import co.cask.common.http.HttpRequest;
import co.cask.common.http.HttpRequests;
import co.cask.common.http.HttpResponse;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Tests whether admin operations work in program contexts.
*/
public class AdminAppTestRun extends TestFrameworkTestBase {
@ClassRule
public static final TestConfiguration CONFIG = new TestConfiguration("explore.enabled", false);
private static final Gson GSON = new Gson();
private ApplicationManager appManager;
@Before
public void deploy() {
appManager = deployApplication(AdminApp.class);
}
@Test
public void testAdminFlow() throws Exception {
// start the worker and wait for it to finish
FlowManager flowManager = appManager.getFlowManager(AdminApp.FLOW_NAME).start();
try {
flowManager.waitForStatus(true, 5, 5);
// send some events to the stream
StreamManager streamManager = getStreamManager("events");
streamManager.send("aa ab bc aa bc");
streamManager.send("xx xy aa ab aa");
// wait for flow to process them
flowManager.getFlowletMetrics("counter").waitForProcessed(10, 30, TimeUnit.SECONDS);
// validate that the flow created tables for a, b, and x, and that the counts are correct
DataSetManager<KeyValueTable> aManager = getDataset("counters_a");
Assert.assertNotNull(aManager.get());
Assert.assertEquals(4L, Bytes.toLong(aManager.get().read("aa")));
Assert.assertEquals(2L, Bytes.toLong(aManager.get().read("ab")));
DataSetManager<KeyValueTable> bManager = getDataset("counters_b");
Assert.assertNotNull(bManager.get());
Assert.assertEquals(2L, Bytes.toLong(bManager.get().read("bc")));
DataSetManager<KeyValueTable> xManager = getDataset("counters_x");
Assert.assertNotNull(xManager.get());
Assert.assertEquals(1L, Bytes.toLong(xManager.get().read("xx")));
Assert.assertEquals(1L, Bytes.toLong(xManager.get().read("xy")));
} finally {
flowManager.stop();
}
flowManager.waitForFinish(30, TimeUnit.SECONDS);
// flowlet destroy() deletes all the tables - validate
Assert.assertNull(getDataset("counters_a").get());
Assert.assertNull(getDataset("counters_b").get());
Assert.assertNull(getDataset("counters_x").get());
}
private interface ProgramStarter<T extends ProgramManager> {
ProgramManager<T> start();
}
@Test
public void testAdminWorker() throws Exception {
testAdminProgram(new ProgramStarter<WorkerManager>() {
@Override
public ProgramManager<WorkerManager> start() {
return appManager.getWorkerManager(AdminApp.WORKER_NAME).start();
}
});
}
@Test
public void testAdminWorkflow() throws Exception {
testAdminProgram(new ProgramStarter<WorkflowManager>() {
@Override
public ProgramManager<WorkflowManager> start() {
return appManager.getWorkflowManager(AdminApp.WORKFLOW_NAME).start();
}
});
}
private <T extends ProgramManager>
void testAdminProgram(ProgramStarter<T> starter) throws Exception {
// create fileset b; it will be updated by the worker
addDatasetInstance(FileSet.class.getName(), "b", FileSetProperties
.builder().setBasePath("some/path").setInputFormat(TextInputFormat.class).build());
DataSetManager<FileSet> bManager = getDataset("b");
String bFormat = bManager.get().getInputFormatClassName();
String bPath = bManager.get().getBaseLocation().toURI().getPath();
Assert.assertTrue(bPath.endsWith("some/path/"));
bManager.flush();
// create table c and write some data to it; it will be truncated by the worker
addDatasetInstance("table", "c");
DataSetManager<Table> cManager = getDataset("c");
cManager.get().put(new Put("x", "y", "z"));
cManager.flush();
// create table d; it will be dropped by the worker
addDatasetInstance("table", "d");
// start the worker and wait for it to finish
ProgramManager<T> manager = starter.start();
manager.waitForFinish(30, TimeUnit.SECONDS);
// validate that worker created dataset a
DataSetManager<Table> aManager = getDataset("a");
Assert.assertNull(aManager.get().scan(null, null).next());
aManager.flush();
// validate that worker update fileset b, Get a new instance of b
bManager = getDataset("b");
Assert.assertEquals(bFormat, bManager.get().getInputFormatClassName());
String newBPath = bManager.get().getBaseLocation().toURI().getPath();
Assert.assertEquals(bPath + "extra/", newBPath);
bManager.flush();
// validate that dataset c is empty
Assert.assertNull(cManager.get().scan(null, null).next());
cManager.flush();
// validate that dataset d is gone
Assert.assertNull(getDataset("d").get());
// run the worker again to drop all datasets
manager.start(ImmutableMap.of("dropAll", "true"));
manager.waitForFinish(30, TimeUnit.SECONDS);
Assert.assertNull(getDataset("a").get());
Assert.assertNull(getDataset("b").get());
Assert.assertNull(getDataset("c").get());
Assert.assertNull(getDataset("d").get());
}
@Test
public void testAdminService() throws Exception {
// Start the service
ServiceManager serviceManager = appManager.getServiceManager(AdminApp.SERVICE_NAME).start();
try {
URI serviceURI = serviceManager.getServiceURL(10, TimeUnit.SECONDS).toURI();
// dataset nn should not exist
HttpResponse response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("exists/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
Assert.assertEquals("false", response.getResponseBodyAsString());
// create nn as a table
response = HttpRequests.execute(HttpRequest.put(serviceURI.resolve("create/nn/table").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
// now nn should exist
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("exists/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
Assert.assertEquals("true", response.getResponseBodyAsString());
// create it again as a fileset -> should fail with conflict
response = HttpRequests.execute(HttpRequest.put(serviceURI.resolve("create/nn/fileSet").toURL()).build());
Assert.assertEquals(409, response.getResponseCode());
// get the type for xx -> not found
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("type/xx").toURL()).build());
Assert.assertEquals(404, response.getResponseCode());
// get the type for nn -> table
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("type/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
Assert.assertEquals("table", response.getResponseBodyAsString());
// update xx's properties -> should get not-found
Map<String, String> nnProps = ImmutableMap.of(Table.PROPERTY_TTL, "1000");
response = HttpRequests.execute(HttpRequest.put(serviceURI.resolve("update/xx").toURL())
.withBody(GSON.toJson(nnProps)).build());
Assert.assertEquals(404, response.getResponseCode());
// update nn's properties
response = HttpRequests.execute(HttpRequest.put(serviceURI.resolve("update/nn").toURL())
.withBody(GSON.toJson(nnProps)).build());
Assert.assertEquals(200, response.getResponseCode());
// get properties for xx -> not found
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("props/xx").toURL()).build());
Assert.assertEquals(404, response.getResponseCode());
// get properties for nn and validate
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("props/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
Map<String, String> returnedProps = GSON.fromJson(response.getResponseBodyAsString(),
new TypeToken<Map<String, String>>() {
}.getType());
Assert.assertEquals(nnProps, returnedProps);
// write some data to the table
DataSetManager<Table> nnManager = getDataset("nn");
nnManager.get().put(new Put("x", "y", "z"));
nnManager.flush();
// in a new tx, validate that data is in table
Assert.assertFalse(nnManager.get().get(new Get("x")).isEmpty());
Assert.assertEquals("z", nnManager.get().get(new Get("x", "y")).getString("y"));
nnManager.flush();
// truncate xx -> not found
response = HttpRequests.execute(HttpRequest.post(serviceURI.resolve("truncate/xx").toURL()).build());
Assert.assertEquals(404, response.getResponseCode());
// truncate nn
response = HttpRequests.execute(HttpRequest.post(serviceURI.resolve("truncate/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
// validate table is empty
Assert.assertTrue(nnManager.get().get(new Get("x")).isEmpty());
nnManager.flush();
// delete nn
response = HttpRequests.execute(HttpRequest.delete(serviceURI.resolve("delete/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
// delete again -> not found
response = HttpRequests.execute(HttpRequest.delete(serviceURI.resolve("delete/nn").toURL()).build());
Assert.assertEquals(404, response.getResponseCode());
// delete xx which never existed -> not found
response = HttpRequests.execute(HttpRequest.delete(serviceURI.resolve("delete/xx").toURL()).build());
Assert.assertEquals(404, response.getResponseCode());
// exists should now return false for nn
response = HttpRequests.execute(HttpRequest.get(serviceURI.resolve("exists/nn").toURL()).build());
Assert.assertEquals(200, response.getResponseCode());
Assert.assertEquals("false", response.getResponseBodyAsString());
Assert.assertNull(getDataset("nn").get());
} finally {
serviceManager.stop();
}
}
@Test
public void testAdminSpark() throws Exception {
testAdminBatchProgram(new ProgramStarter<SparkManager>() {
@Override
public ProgramManager<SparkManager> start() {
return appManager.getSparkManager(AdminApp.SPARK_NAME).start();
}
});
}
@Test
public void testAdminScalaSpark() throws Exception {
testAdminBatchProgram(new ProgramStarter<SparkManager>() {
@Override
public ProgramManager<SparkManager> start() {
return appManager.getSparkManager(AdminApp.SPARK_SCALA_NAME).start();
}
});
}
@Test
public void testAdminMapReduce() throws Exception {
testAdminBatchProgram(new ProgramStarter<MapReduceManager>() {
@Override
public ProgramManager<MapReduceManager> start() {
return appManager.getMapReduceManager(AdminApp.MAPREDUCE_NAME).start();
}
});
}
private <T extends ProgramManager>
void testAdminBatchProgram(ProgramStarter<T> starter) throws Exception {
addDatasetInstance("keyValueTable", "lines");
addDatasetInstance("keyValueTable", "counts");
// add some lines to the input dataset
DataSetManager<KeyValueTable> linesManager = getDataset("lines");
linesManager.get().write("1", "hello world");
linesManager.get().write("2", "hi world");
linesManager.flush();
// add some counts to the output dataset
DataSetManager<KeyValueTable> countsManager = getDataset("counts");
countsManager.get().write("you", Bytes.toBytes(5));
countsManager.get().write("me", Bytes.toBytes(3));
countsManager.flush();
ProgramManager<T> manager = starter.start();
manager.waitForFinish(60, TimeUnit.SECONDS);
// validate that there are no counts for "you" and "me", and the the other counts are accurate
countsManager.flush(); // need to start a new tx to see the output of MR
Assert.assertEquals(2, Bytes.toInt(countsManager.get().read("world")));
Assert.assertEquals(1, Bytes.toInt(countsManager.get().read("hello")));
Assert.assertEquals(1, Bytes.toInt(countsManager.get().read("hi")));
Assert.assertNull(countsManager.get().read("you"));
Assert.assertNull(countsManager.get().read("me"));
countsManager.flush();
}
}