/*
* 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.data2.datafabric.dataset;
import co.cask.cdap.api.dataset.DatasetManagementException;
import co.cask.cdap.api.dataset.module.DatasetModule;
import co.cask.cdap.api.metrics.MetricsCollectionService;
import co.cask.cdap.common.conf.CConfigurationUtil;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.metrics.NoOpMetricsCollectionService;
import co.cask.cdap.common.namespace.DefaultNamespacedLocationFactory;
import co.cask.cdap.common.namespace.NamespacedLocationFactory;
import co.cask.cdap.data.dataset.SystemDatasetInstantiatorFactory;
import co.cask.cdap.data2.datafabric.dataset.instance.DatasetInstanceManager;
import co.cask.cdap.data2.datafabric.dataset.service.DatasetInstanceService;
import co.cask.cdap.data2.datafabric.dataset.service.DatasetService;
import co.cask.cdap.data2.datafabric.dataset.service.LocalStorageProviderNamespaceAdmin;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetAdminOpHTTPHandler;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetAdminService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.DatasetOpExecutorService;
import co.cask.cdap.data2.datafabric.dataset.service.executor.InMemoryDatasetOpExecutor;
import co.cask.cdap.data2.datafabric.dataset.service.mds.MDSDatasetsRegistry;
import co.cask.cdap.data2.datafabric.dataset.type.DatasetTypeManager;
import co.cask.cdap.data2.dataset2.AbstractDatasetFrameworkTest;
import co.cask.cdap.data2.dataset2.DatasetFramework;
import co.cask.cdap.data2.dataset2.InMemoryDatasetFramework;
import co.cask.cdap.data2.dataset2.SimpleKVTable;
import co.cask.cdap.data2.dataset2.SingleTypeModule;
import co.cask.cdap.data2.dataset2.lib.table.CoreDatasetsModule;
import co.cask.cdap.data2.dataset2.module.lib.inmemory.InMemoryTableModule;
import co.cask.cdap.data2.metadata.store.NoOpMetadataStore;
import co.cask.cdap.data2.metrics.DatasetMetricsReporter;
import co.cask.cdap.data2.transaction.DelegatingTransactionSystemClientService;
import co.cask.cdap.data2.transaction.TransactionSystemClientService;
import co.cask.cdap.explore.client.DiscoveryExploreClient;
import co.cask.cdap.explore.client.ExploreFacade;
import co.cask.cdap.proto.Id;
import co.cask.http.HttpHandler;
import co.cask.tephra.TransactionManager;
import co.cask.tephra.inmemory.InMemoryTxSystemClient;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.twill.common.Threads;
import org.apache.twill.discovery.InMemoryDiscoveryService;
import org.apache.twill.discovery.ServiceDiscovered;
import org.apache.twill.filesystem.LocalLocationFactory;
import org.apache.twill.internal.Services;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Tests for {@link RemoteDatasetFramework}
*/
public class RemoteDatasetFrameworkTest extends AbstractDatasetFrameworkTest {
private TransactionManager txManager;
private DatasetOpExecutorService opExecutorService;
private DatasetService service;
private RemoteDatasetFramework framework;
@ClassRule
public static TemporaryFolder tmpFolder = new TemporaryFolder();
@Before
public void before() throws Exception {
File dataDir = new File(tmpFolder.newFolder(), "data");
cConf.set(Constants.CFG_LOCAL_DATA_DIR, dataDir.getAbsolutePath());
cConf.set(Constants.Dataset.Manager.ADDRESS, "localhost");
cConf.setBoolean(Constants.Dangerous.UNRECOVERABLE_RESET, true);
// Starting DatasetService service
InMemoryDiscoveryService discoveryService = new InMemoryDiscoveryService();
MetricsCollectionService metricsCollectionService = new NoOpMetricsCollectionService();
// Tx Manager to support working with datasets
Configuration txConf = HBaseConfiguration.create();
CConfigurationUtil.copyTxProperties(cConf, txConf);
txManager = new TransactionManager(txConf);
txManager.startAndWait();
InMemoryTxSystemClient txSystemClient = new InMemoryTxSystemClient(txManager);
TransactionSystemClientService txSystemClientService = new DelegatingTransactionSystemClientService(txSystemClient);
LocalLocationFactory locationFactory = new LocalLocationFactory(new File(cConf.get(Constants.CFG_LOCAL_DATA_DIR)));
NamespacedLocationFactory namespacedLocationFactory = new DefaultNamespacedLocationFactory(cConf, locationFactory);
framework = new RemoteDatasetFramework(cConf, discoveryService, registryFactory);
SystemDatasetInstantiatorFactory datasetInstantiatorFactory =
new SystemDatasetInstantiatorFactory(locationFactory, framework, cConf);
DatasetAdminService datasetAdminService =
new DatasetAdminService(framework, cConf, locationFactory, datasetInstantiatorFactory, new NoOpMetadataStore());
ImmutableSet<HttpHandler> handlers =
ImmutableSet.<HttpHandler>of(new DatasetAdminOpHTTPHandler(datasetAdminService));
opExecutorService = new DatasetOpExecutorService(cConf, discoveryService, metricsCollectionService, handlers);
opExecutorService.startAndWait();
ImmutableMap<String, DatasetModule> modules = ImmutableMap.<String, DatasetModule>builder()
.put("memoryTable", new InMemoryTableModule())
.put("core", new CoreDatasetsModule())
.putAll(DatasetMetaTableUtil.getModules())
.build();
InMemoryDatasetFramework mdsFramework = new InMemoryDatasetFramework(registryFactory, modules, cConf);
MDSDatasetsRegistry mdsDatasetsRegistry = new MDSDatasetsRegistry(txSystemClientService, mdsFramework);
ExploreFacade exploreFacade = new ExploreFacade(new DiscoveryExploreClient(cConf, discoveryService), cConf);
DatasetInstanceService instanceService = new DatasetInstanceService(
new DatasetTypeManager(cConf, mdsDatasetsRegistry, locationFactory, DEFAULT_MODULES),
new DatasetInstanceManager(mdsDatasetsRegistry),
new InMemoryDatasetOpExecutor(framework),
exploreFacade,
cConf,
txExecutorFactory,
registryFactory,
NAMESPACE_STORE);
instanceService.setAuditPublisher(inMemoryAuditPublisher);
service = new DatasetService(cConf,
namespacedLocationFactory,
discoveryService,
discoveryService,
new DatasetTypeManager(cConf, mdsDatasetsRegistry, locationFactory, DEFAULT_MODULES),
metricsCollectionService,
new InMemoryDatasetOpExecutor(framework),
mdsDatasetsRegistry,
new HashSet<DatasetMetricsReporter>(),
instanceService,
new LocalStorageProviderNamespaceAdmin(cConf, namespacedLocationFactory,
exploreFacade),
NAMESPACE_STORE
);
// Start dataset service, wait for it to be discoverable
service.start();
final CountDownLatch startLatch = new CountDownLatch(1);
discoveryService.discover(Constants.Service.DATASET_MANAGER).watchChanges(new ServiceDiscovered.ChangeListener() {
@Override
public void onChange(ServiceDiscovered serviceDiscovered) {
if (!Iterables.isEmpty(serviceDiscovered)) {
startLatch.countDown();
}
}
}, Threads.SAME_THREAD_EXECUTOR);
startLatch.await(5, TimeUnit.SECONDS);
framework.createNamespace(Id.Namespace.SYSTEM);
framework.createNamespace(NAMESPACE_ID);
}
// Note: Cannot have these system namespace restrictions in system namespace since we use it internally in
// DatasetMetaTable util to add modules to system namespace. However, we should definitely impose these restrictions
// in RemoteDatasetFramework.
@Test
public void testSystemNamespace() throws DatasetManagementException {
DatasetFramework framework = getFramework();
// Adding module to system namespace should fail
try {
framework.addModule(Id.DatasetModule.from(Id.Namespace.SYSTEM, "keyValue"),
new SingleTypeModule(SimpleKVTable.class));
Assert.fail("Should not be able to add a module to system namespace");
} catch (DatasetManagementException e) {
// expected
}
try {
framework.deleteModule(Id.DatasetModule.from(Id.Namespace.SYSTEM, "orderedTable-memory"));
Assert.fail("Should not be able to delete a default module.");
} catch (DatasetManagementException e) {
// expected
}
try {
framework.deleteAllModules(Id.Namespace.SYSTEM);
Assert.fail("Should not be able to delete modules from system namespace");
} catch (DatasetManagementException e) {
// expected
}
}
@After
public void after() throws DatasetManagementException {
framework.deleteNamespace(NAMESPACE_ID);
framework.deleteNamespace(Id.Namespace.SYSTEM);
Futures.getUnchecked(Services.chainStop(service, opExecutorService, txManager));
}
@Override
protected DatasetFramework getFramework() {
return framework;
}
}