/* * 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.scheduler; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.nio.file.Files; import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ServiceManager; import gobblin.configuration.ConfigurationKeys; import gobblin.testing.AssertWithBackoff; /** * Unit tests for the job configuration file monitor in {@link gobblin.scheduler.JobScheduler}. * * @author Yinan Li */ @Test(groups = {"gobblin.scheduler"}) public class JobConfigFileMonitorTest { private static final String JOB_CONFIG_FILE_DIR = "gobblin-test/resource/job-conf"; private String jobConfigDir; private ServiceManager serviceManager; private JobScheduler jobScheduler; private File newJobConfigFile; private class GetNumScheduledJobs implements Function<Void, Integer> { @Override public Integer apply(Void input) { return JobConfigFileMonitorTest.this.jobScheduler.getScheduledJobs().size(); } } @BeforeClass public void setUp() throws Exception { this.jobConfigDir = Files.createTempDirectory(String.format("gobblin-test_%s_job-conf", this.getClass().getSimpleName())) .toString(); FileUtils.forceDeleteOnExit(new File(this.jobConfigDir)); FileUtils.copyDirectory(new File(JOB_CONFIG_FILE_DIR), new File(jobConfigDir)); Properties properties = new Properties(); try (Reader schedulerPropsReader = new FileReader("gobblin-test/resource/gobblin.test.properties")) { properties.load(schedulerPropsReader); } properties.setProperty(ConfigurationKeys.JOB_CONFIG_FILE_DIR_KEY, jobConfigDir); properties.setProperty(ConfigurationKeys.JOB_CONFIG_FILE_GENERAL_PATH_KEY, jobConfigDir); properties.setProperty(ConfigurationKeys.JOB_CONFIG_FILE_MONITOR_POLLING_INTERVAL_KEY, "1000"); properties.setProperty(ConfigurationKeys.METRICS_ENABLED_KEY, "false"); SchedulerService quartzService = new SchedulerService(new Properties()); this.jobScheduler = new JobScheduler(properties, quartzService); this.serviceManager = new ServiceManager(Lists.newArrayList(quartzService, this.jobScheduler)); this.serviceManager.startAsync().awaitHealthy(10, TimeUnit.SECONDS);; } @Test public void testAddNewJobConfigFile() throws Exception { final Logger log = LoggerFactory.getLogger("testAddNewJobConfigFile"); log.info("testAddNewJobConfigFile: start"); AssertWithBackoff assertWithBackoff = AssertWithBackoff.create().logger(log).timeoutMs(15000); assertWithBackoff.assertEquals(new GetNumScheduledJobs(), 3, "3 scheduled jobs"); /* Set a time gap, to let the monitor recognize the "3-file" status as old status, so that new added file can be discovered */ Thread.sleep(1000); // Create a new job configuration file by making a copy of an existing // one and giving a different job name Properties jobProps = new Properties(); jobProps.load(new FileReader(new File(this.jobConfigDir, "GobblinTest1.pull"))); jobProps.setProperty(ConfigurationKeys.JOB_NAME_KEY, "Gobblin-test-new"); this.newJobConfigFile = new File(this.jobConfigDir, "Gobblin-test-new.pull"); jobProps.store(new FileWriter(this.newJobConfigFile), null); assertWithBackoff.assertEquals(new GetNumScheduledJobs(), 4, "4 scheduled jobs"); Set<String> jobNames = Sets.newHashSet(this.jobScheduler.getScheduledJobs()); Set<String> expectedJobNames = ImmutableSet.<String>builder() .add("GobblinTest1", "GobblinTest2", "GobblinTest3", "Gobblin-test-new") .build(); Assert.assertEquals(jobNames, expectedJobNames); log.info("testAddNewJobConfigFile: end"); } @Test(dependsOnMethods = {"testAddNewJobConfigFile"}) public void testChangeJobConfigFile() throws Exception { final Logger log = LoggerFactory.getLogger("testChangeJobConfigFile"); log.info("testChangeJobConfigFile: start"); Assert.assertEquals(this.jobScheduler.getScheduledJobs().size(), 4); // Make a change to the new job configuration file Properties jobProps = new Properties(); jobProps.load(new FileReader(this.newJobConfigFile)); jobProps.setProperty(ConfigurationKeys.JOB_COMMIT_POLICY_KEY, "partial"); jobProps.setProperty(ConfigurationKeys.JOB_NAME_KEY, "Gobblin-test-new2"); jobProps.store(new FileWriter(this.newJobConfigFile), null); AssertWithBackoff.create() .logger(log) .timeoutMs(30000) .assertEquals(new GetNumScheduledJobs(), 4, "4 scheduled jobs"); final Set<String> expectedJobNames = ImmutableSet.<String>builder() .add("GobblinTest1", "GobblinTest2", "GobblinTest3", "Gobblin-test-new2") .build(); AssertWithBackoff.create() .logger(log) .timeoutMs(30000) .assertEquals(new Function<Void, Set<String>>() { @Override public Set<String> apply(Void input) { return Sets.newHashSet(JobConfigFileMonitorTest.this.jobScheduler.getScheduledJobs()); } }, expectedJobNames, "Job change detected"); log.info("testChangeJobConfigFile: end"); } @Test(dependsOnMethods = {"testChangeJobConfigFile"}) public void testUnscheduleJob() throws Exception { final Logger log = LoggerFactory.getLogger("testUnscheduleJob"); log.info("testUnscheduleJob: start"); Assert.assertEquals(this.jobScheduler.getScheduledJobs().size(), 4); // Disable the new job by setting job.disabled=true Properties jobProps = new Properties(); jobProps.load(new FileReader(this.newJobConfigFile)); jobProps.setProperty(ConfigurationKeys.JOB_DISABLED_KEY, "true"); jobProps.store(new FileWriter(this.newJobConfigFile), null); AssertWithBackoff.create() .logger(log) .timeoutMs(7500) .assertEquals(new GetNumScheduledJobs(), 3, "3 scheduled jobs"); Set<String> jobNames = Sets.newHashSet(this.jobScheduler.getScheduledJobs()); Assert.assertEquals(jobNames.size(), 3); Assert.assertTrue(jobNames.contains("GobblinTest1")); Assert.assertTrue(jobNames.contains("GobblinTest2")); Assert.assertTrue(jobNames.contains("GobblinTest3")); log.info("testUnscheduleJob: end"); } @AfterClass public void tearDown() throws TimeoutException, IOException { if (jobConfigDir != null) { FileUtils.forceDelete(new File(jobConfigDir)); } this.serviceManager.stopAsync().awaitStopped(30, TimeUnit.SECONDS); } }