/*
* 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.runtime.instance;
import java.net.URI;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.typesafe.config.ConfigFactory;
import gobblin.runtime.api.GobblinInstanceDriver;
import gobblin.runtime.api.JobExecutionDriver;
import gobblin.runtime.api.JobExecutionLauncher;
import gobblin.runtime.api.JobExecutionResult;
import gobblin.runtime.api.JobLifecycleListener;
import gobblin.runtime.api.JobSpec;
import gobblin.runtime.instance.DefaultGobblinInstanceDriverImpl.JobSpecRunnable;
import gobblin.runtime.job_spec.ResolvedJobSpec;
import gobblin.runtime.std.DefaultJobLifecycleListenerImpl;
import gobblin.runtime.std.FilteredJobLifecycleListener;
import gobblin.runtime.std.JobSpecFilter;
import gobblin.testing.AssertWithBackoff;
import gobblin.util.test.HelloWorldSource;
import gobblin.writer.test.GobblinTestEventBusWriter;
import gobblin.writer.test.TestingEventBusAsserter;
/**
* Unit tests for {@link StandardGobblinInstanceLauncher}
*/
public class TestStandardGobblinInstanceLauncher {
@Test
/** Test running of a job when submitted directly to the execution driver*/
public void testDirectToExecutionDriver() throws Exception {
StandardGobblinInstanceLauncher.Builder instanceLauncherBuilder =
StandardGobblinInstanceLauncher.builder()
.withInstanceName("testDirectToExecutionDriver");
instanceLauncherBuilder.driver();
StandardGobblinInstanceLauncher instanceLauncher =
instanceLauncherBuilder.build();
instanceLauncher.startAsync();
instanceLauncher.awaitRunning(5, TimeUnit.SECONDS);
JobSpec js1 = JobSpec.builder()
.withConfig(ConfigFactory.parseResources("gobblin/runtime/instance/SimpleHelloWorldJob.jobconf"))
.build();
GobblinInstanceDriver instance = instanceLauncher.getDriver();
final JobExecutionLauncher.StandardMetrics launcherMetrics =
instance.getJobLauncher().getMetrics();
AssertWithBackoff asb = new AssertWithBackoff().timeoutMs(100);
checkLaunchJob(instanceLauncher, js1, instance);
Assert.assertEquals(launcherMetrics.getNumJobsLaunched().getCount(), 1);
Assert.assertEquals(launcherMetrics.getNumJobsCompleted().getCount(), 1);
// Need to use assert with backoff because of race conditions with the callback that updates the
// metrics
asb.assertEquals(new Function<Void, Long>() {
@Override public Long apply(Void input) {
return launcherMetrics.getNumJobsCommitted().getCount();
}
}, 1l, "numJobsCommitted==1");
Assert.assertEquals(launcherMetrics.getNumJobsFailed().getCount(), 0);
Assert.assertEquals(launcherMetrics.getNumJobsRunning().getValue().intValue(), 0);
checkLaunchJob(instanceLauncher, js1, instance);
Assert.assertEquals(launcherMetrics.getNumJobsLaunched().getCount(), 2);
Assert.assertEquals(launcherMetrics.getNumJobsCompleted().getCount(), 2);
asb.assertEquals(new Function<Void, Long>() {
@Override public Long apply(Void input) {
return launcherMetrics.getNumJobsCommitted().getCount();
}
}, 2l, "numJobsCommitted==2");
Assert.assertEquals(launcherMetrics.getNumJobsFailed().getCount(), 0);
Assert.assertEquals(launcherMetrics.getNumJobsRunning().getValue().intValue(), 0);
}
private void checkLaunchJob(StandardGobblinInstanceLauncher instanceLauncher, JobSpec js1,
GobblinInstanceDriver instance) throws TimeoutException, InterruptedException, ExecutionException {
JobExecutionDriver jobDriver = instance.getJobLauncher().launchJob(js1);
new Thread(jobDriver).run();
JobExecutionResult jobResult = jobDriver.get(5, TimeUnit.SECONDS);
Assert.assertTrue(jobResult.isSuccessful());
instanceLauncher.stopAsync();
instanceLauncher.awaitTerminated(5, TimeUnit.SECONDS);
Assert.assertEquals(instance.getMetrics().getUpFlag().getValue().intValue(), 0);
Assert.assertEquals(instance.getMetrics().getUptimeMs().getValue().longValue(), 0);
}
@Test
/** Test running of a job when submitted directly to the scheduler */
public void testDirectToScheduler() throws Exception {
StandardGobblinInstanceLauncher.Builder instanceLauncherBuilder =
StandardGobblinInstanceLauncher.builder()
.withInstanceName("testDirectToScheduler");
instanceLauncherBuilder.driver();
StandardGobblinInstanceLauncher instanceLauncher =
instanceLauncherBuilder.build();
instanceLauncher.startAsync();
instanceLauncher.awaitRunning(5, TimeUnit.SECONDS);
JobSpec js1 = JobSpec.builder()
.withConfig(ConfigFactory.parseResources("gobblin/runtime/instance/SimpleHelloWorldJob.jobconf"))
.build();
final StandardGobblinInstanceDriver instance =
(StandardGobblinInstanceDriver)instanceLauncher.getDriver();
final ArrayBlockingQueue<JobExecutionDriver> jobDrivers = new ArrayBlockingQueue<>(1);
JobLifecycleListener js1Listener = new FilteredJobLifecycleListener(
JobSpecFilter.eqJobSpecURI(js1.getUri()),
new DefaultJobLifecycleListenerImpl(instance.getLog()) {
@Override public void onJobLaunch(JobExecutionDriver jobDriver) {
super.onJobLaunch(jobDriver);
try {
jobDrivers.offer(jobDriver, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
instance.getLog().error("Offer interrupted.");
}
}
});
instance.registerWeakJobLifecycleListener(js1Listener);
JobSpecRunnable js1Runnable = instance.createJobSpecRunnable(js1);
instance.getJobScheduler().scheduleOnce(js1, js1Runnable);
JobExecutionDriver jobDriver = jobDrivers.poll(10, TimeUnit.SECONDS);
Assert.assertNotNull(jobDriver);
JobExecutionResult jobResult = jobDriver.get(5, TimeUnit.SECONDS);
Assert.assertTrue(jobResult.isSuccessful());
instanceLauncher.stopAsync();
instanceLauncher.awaitTerminated(5, TimeUnit.SECONDS);
}
@Test
/** Test running of a job using the standard path of submitting to the job catalog */
public void testSubmitToJobCatalog() throws Exception {
StandardGobblinInstanceLauncher.Builder instanceLauncherBuilder =
StandardGobblinInstanceLauncher.builder()
.withInstanceName("testSubmitToJobCatalog");
instanceLauncherBuilder.driver();
StandardGobblinInstanceLauncher instanceLauncher =
instanceLauncherBuilder.build();
instanceLauncher.startAsync();
instanceLauncher.awaitRunning(5, TimeUnit.SECONDS);
JobSpec js1 = JobSpec.builder()
.withConfig(ConfigFactory.parseResources("gobblin/runtime/instance/SimpleHelloWorldJob.jobconf"))
.build();
final String eventBusId = js1.getConfig().resolve().getString(GobblinTestEventBusWriter.FULL_EVENTBUSID_KEY);
TestingEventBusAsserter asserter = new TestingEventBusAsserter(eventBusId);
final StandardGobblinInstanceDriver instance =
(StandardGobblinInstanceDriver)instanceLauncher.getDriver();
final ArrayBlockingQueue<JobExecutionDriver> jobDrivers = new ArrayBlockingQueue<>(1);
JobLifecycleListener js1Listener = new FilteredJobLifecycleListener(
JobSpecFilter.eqJobSpecURI(js1.getUri()),
new DefaultJobLifecycleListenerImpl(instance.getLog()) {
@Override public void onJobLaunch(JobExecutionDriver jobDriver) {
super.onJobLaunch(jobDriver);
try {
jobDrivers.offer(jobDriver, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
instance.getLog().error("Offer interrupted.");
}
}
});
instance.registerWeakJobLifecycleListener(js1Listener);
instance.getMutableJobCatalog().put(js1);
JobExecutionDriver jobDriver = jobDrivers.poll(10, TimeUnit.SECONDS);
Assert.assertNotNull(jobDriver);
JobExecutionResult jobResult = jobDriver.get(5, TimeUnit.SECONDS);
Assert.assertTrue(jobResult.isSuccessful());
instanceLauncher.stopAsync();
final int numHellos = js1.getConfig().getInt(HelloWorldSource.NUM_HELLOS_FULL_KEY);
ArrayList<String> expectedEvents = new ArrayList<>();
for (int i = 1; i <= numHellos; ++i) {
expectedEvents.add(HelloWorldSource.ExtractorImpl.helloMessage(i));
}
asserter.assertNextValuesEq(expectedEvents);
asserter.close();
instanceLauncher.awaitTerminated(5, TimeUnit.SECONDS);
}
@Test
public void testSubmitWithTemplate() throws Exception {
StandardGobblinInstanceLauncher.Builder instanceLauncherBuilder =
StandardGobblinInstanceLauncher.builder()
.withInstanceName("testSubmitWithTemplate");
instanceLauncherBuilder.driver();
StandardGobblinInstanceLauncher instanceLauncher =
instanceLauncherBuilder.build();
instanceLauncher.startAsync();
instanceLauncher.awaitRunning(5, TimeUnit.SECONDS);
JobSpec js1 = JobSpec.builder()
.withConfig(ConfigFactory.parseMap(ImmutableMap.of("numHellos", "5")))
.withTemplate(new URI("resource:///gobblin/runtime/instance/SimpleHelloWorldJob.template"))
.build();
ResolvedJobSpec js1Resolved = new ResolvedJobSpec(js1);
final String eventBusId = js1Resolved.getConfig().getString(GobblinTestEventBusWriter.FULL_EVENTBUSID_KEY);
TestingEventBusAsserter asserter = new TestingEventBusAsserter(eventBusId);
final StandardGobblinInstanceDriver instance =
(StandardGobblinInstanceDriver)instanceLauncher.getDriver();
final ArrayBlockingQueue<JobExecutionDriver> jobDrivers = new ArrayBlockingQueue<>(1);
JobLifecycleListener js1Listener = new FilteredJobLifecycleListener(
JobSpecFilter.eqJobSpecURI(js1.getUri()),
new DefaultJobLifecycleListenerImpl(instance.getLog()) {
@Override public void onJobLaunch(JobExecutionDriver jobDriver) {
super.onJobLaunch(jobDriver);
try {
jobDrivers.offer(jobDriver, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
instance.getLog().error("Offer interrupted.");
}
}
});
instance.registerWeakJobLifecycleListener(js1Listener);
instance.getMutableJobCatalog().put(js1);
JobExecutionDriver jobDriver = jobDrivers.poll(10, TimeUnit.SECONDS);
Assert.assertNotNull(jobDriver);
JobExecutionResult jobResult = jobDriver.get(5, TimeUnit.SECONDS);
Assert.assertTrue(jobResult.isSuccessful());
instanceLauncher.stopAsync();
final int numHellos = js1Resolved.getConfig().getInt(HelloWorldSource.NUM_HELLOS_FULL_KEY);
ArrayList<String> expectedEvents = new ArrayList<>();
for (int i = 1; i <= numHellos; ++i) {
expectedEvents.add(HelloWorldSource.ExtractorImpl.helloMessage(i));
}
asserter.assertNextValuesEq(expectedEvents);
asserter.close();
instanceLauncher.awaitTerminated(5, TimeUnit.SECONDS);
}
}