/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 gobblin.cluster;
import com.typesafe.config.ConfigFactory;
import gobblin.metastore.FsStateStore;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import org.apache.avro.Schema;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.helix.HelixManager;
import org.apache.helix.task.TaskCallbackContext;
import org.apache.helix.task.TaskConfig;
import org.apache.helix.task.TaskResult;
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import gobblin.configuration.ConfigurationKeys;
import gobblin.example.simplejson.SimpleJsonConverter;
import gobblin.example.simplejson.SimpleJsonSource;
import gobblin.runtime.AbstractJobLauncher;
import gobblin.runtime.JobState;
import gobblin.runtime.TaskExecutor;
import gobblin.source.workunit.WorkUnit;
import gobblin.util.Id;
import gobblin.util.SerializationUtils;
import gobblin.writer.AvroDataWriterBuilder;
import gobblin.writer.Destination;
import gobblin.writer.WriterOutputFormat;
/**
* Unit tests for {@link GobblinHelixTask}.
*
* <p>
* This class uses a mocked {@link HelixManager} to control the behavior of certain method for testing.
* A {@link TaskExecutor} is used to run the test task and a {@link GobblinHelixTaskStateTracker} is
* also used as being required by {@link GobblinHelixTaskFactory}. The test task writes everything to
* the local file system as returned by {@link FileSystem#getLocal(Configuration)}.
* </p>
*
* @author Yinan Li
*/
@Test(groups = { "gobblin.cluster" })
public class GobblinHelixTaskTest {
private TaskExecutor taskExecutor;
private GobblinHelixTaskStateTracker taskStateTracker;
private GobblinHelixTask gobblinHelixTask;
private HelixManager helixManager;
private FileSystem localFs;
private Path appWorkDir;
private Path taskOutputDir;
@BeforeClass
public void setUp() throws IOException {
Configuration configuration = new Configuration();
configuration.setInt(ConfigurationKeys.TASK_EXECUTOR_THREADPOOL_SIZE_KEY, 1);
this.taskExecutor = new TaskExecutor(configuration);
this.helixManager = Mockito.mock(HelixManager.class);
Mockito.when(this.helixManager.getInstanceName()).thenReturn(GobblinHelixTaskTest.class.getSimpleName());
this.taskStateTracker = new GobblinHelixTaskStateTracker(new Properties(), this.helixManager);
this.localFs = FileSystem.getLocal(configuration);
this.appWorkDir = new Path(GobblinHelixTaskTest.class.getSimpleName());
this.taskOutputDir = new Path(this.appWorkDir, "output");
}
@Test
public void testPrepareTask() throws IOException {
// Serialize the JobState that will be read later in GobblinHelixTask
Path jobStateFilePath =
new Path(appWorkDir, TestHelper.TEST_JOB_ID + "." + AbstractJobLauncher.JOB_STATE_FILE_NAME);
JobState jobState = new JobState();
jobState.setJobName(TestHelper.TEST_JOB_NAME);
jobState.setJobId(TestHelper.TEST_JOB_ID);
SerializationUtils.serializeState(this.localFs, jobStateFilePath, jobState);
// Prepare the WorkUnit
WorkUnit workUnit = WorkUnit.createEmpty();
prepareWorkUnit(workUnit);
// Prepare the source Json file
File sourceJsonFile = new File(this.appWorkDir.toString(), TestHelper.TEST_JOB_NAME + ".json");
TestHelper.createSourceJsonFile(sourceJsonFile);
workUnit.setProp(SimpleJsonSource.SOURCE_FILE_KEY, sourceJsonFile.getAbsolutePath());
// Serialize the WorkUnit into a file
// expected path is appWorkDir/_workunits/job_id/job_id.wu
Path workUnitDirPath = new Path(this.appWorkDir, GobblinClusterConfigurationKeys.INPUT_WORK_UNIT_DIR_NAME);
FsStateStore<WorkUnit> wuStateStore = new FsStateStore<>(this.localFs, workUnitDirPath.toString(), WorkUnit.class);
Path workUnitFilePath = new Path(new Path(workUnitDirPath, TestHelper.TEST_JOB_ID),
TestHelper.TEST_JOB_NAME + ".wu");
wuStateStore.put(TestHelper.TEST_JOB_ID, TestHelper.TEST_JOB_NAME + ".wu", workUnit);
Assert.assertTrue(this.localFs.exists(workUnitFilePath));
// Prepare the GobblinHelixTask
Map<String, String> taskConfigMap = Maps.newHashMap();
taskConfigMap.put(GobblinClusterConfigurationKeys.WORK_UNIT_FILE_PATH, workUnitFilePath.toString());
taskConfigMap.put(ConfigurationKeys.JOB_NAME_KEY, TestHelper.TEST_JOB_NAME);
taskConfigMap.put(ConfigurationKeys.JOB_ID_KEY, TestHelper.TEST_JOB_ID);
taskConfigMap.put(ConfigurationKeys.TASK_KEY_KEY, Long.toString(Id.parse(TestHelper.TEST_JOB_ID).getSequence()));
TaskConfig taskConfig = new TaskConfig("", taskConfigMap, true);
TaskCallbackContext taskCallbackContext = Mockito.mock(TaskCallbackContext.class);
Mockito.when(taskCallbackContext.getTaskConfig()).thenReturn(taskConfig);
Mockito.when(taskCallbackContext.getManager()).thenReturn(this.helixManager);
GobblinHelixTaskFactory gobblinHelixTaskFactory =
new GobblinHelixTaskFactory(Optional.<ContainerMetrics>absent(), this.taskExecutor, this.taskStateTracker,
this.localFs, this.appWorkDir, ConfigFactory.empty());
this.gobblinHelixTask = (GobblinHelixTask) gobblinHelixTaskFactory.createNewTask(taskCallbackContext);
}
@Test(dependsOnMethods = "testPrepareTask")
public void testRun() throws IOException {
TaskResult taskResult = this.gobblinHelixTask.run();
System.out.println(taskResult.getInfo());
Assert.assertEquals(taskResult.getStatus(), TaskResult.Status.COMPLETED);
File outputAvroFile = new File(this.taskOutputDir.toString(),
TestHelper.REL_WRITER_FILE_PATH + File.separator + TestHelper.WRITER_FILE_NAME);
Assert.assertTrue(outputAvroFile.exists());
Schema schema = new Schema.Parser().parse(TestHelper.SOURCE_SCHEMA);
TestHelper.assertGenericRecords(outputAvroFile, schema);
}
@AfterClass
public void tearDown() throws IOException {
try {
if (this.localFs.exists(this.appWorkDir)) {
this.localFs.delete(this.appWorkDir, true);
}
} finally {
this.taskExecutor.stopAsync().awaitTerminated();
this.taskStateTracker.stopAsync().awaitTerminated();
}
}
private void prepareWorkUnit(WorkUnit workUnit) {
workUnit.setProp(ConfigurationKeys.TASK_ID_KEY, TestHelper.TEST_TASK_ID);
workUnit.setProp(ConfigurationKeys.TASK_KEY_KEY, Long.toString(Id.parse(TestHelper.TEST_TASK_ID).getSequence()));
workUnit.setProp(ConfigurationKeys.SOURCE_CLASS_KEY, SimpleJsonSource.class.getName());
workUnit.setProp(ConfigurationKeys.CONVERTER_CLASSES_KEY, SimpleJsonConverter.class.getName());
workUnit.setProp(ConfigurationKeys.WRITER_OUTPUT_FORMAT_KEY, WriterOutputFormat.AVRO.toString());
workUnit.setProp(ConfigurationKeys.WRITER_DESTINATION_TYPE_KEY, Destination.DestinationType.HDFS.toString());
workUnit.setProp(ConfigurationKeys.WRITER_STAGING_DIR, this.appWorkDir.toString() + Path.SEPARATOR + "staging");
workUnit.setProp(ConfigurationKeys.WRITER_OUTPUT_DIR, this.taskOutputDir.toString());
workUnit.setProp(ConfigurationKeys.WRITER_FILE_NAME, TestHelper.WRITER_FILE_NAME);
workUnit.setProp(ConfigurationKeys.WRITER_FILE_PATH, TestHelper.REL_WRITER_FILE_PATH);
workUnit.setProp(ConfigurationKeys.WRITER_BUILDER_CLASS, AvroDataWriterBuilder.class.getName());
workUnit.setProp(ConfigurationKeys.SOURCE_SCHEMA, TestHelper.SOURCE_SCHEMA);
}
}