/* * Copyright 2017 ThoughtWorks, 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 com.thoughtworks.go.server.service; import java.sql.SQLException; import java.util.Arrays; import java.util.Date; import com.thoughtworks.go.config.*; import com.thoughtworks.go.domain.*; import com.thoughtworks.go.domain.activity.JobStatusCache; import com.thoughtworks.go.fixture.PipelineWithTwoStages; import com.thoughtworks.go.server.dao.DatabaseAccessHelper; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.util.GoConfigFileHelper; import com.thoughtworks.go.util.ReflectionUtil; import com.thoughtworks.go.util.TimeProvider; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:WEB-INF/applicationContext-global.xml", "classpath:WEB-INF/applicationContext-dataLocalAccess.xml", "classpath:WEB-INF/applicationContext-acegi-security.xml" }) public class RescheduleJobTest { @Autowired private GoConfigDao goConfigDao; @Autowired private DatabaseAccessHelper dbHelper; private static GoConfigFileHelper configHelper = new GoConfigFileHelper(); private PipelineWithTwoStages fixture; @Autowired private JobInstanceService jobInstanceService; @Autowired private ScheduleService scheduleService; @Autowired private JobStatusCache jobStatusCache; @Autowired private MaterialRepository materialRepository; @Autowired private TransactionTemplate transactionTemplate; public static final String JOB_NAME = "unit"; private static final String STAGE_NAME = "mingle"; private static final String PIPELINE_NAME = "studios"; private Stage stage; @Before public void setUp() throws Exception { configHelper.usingCruiseConfigDao(goConfigDao); configHelper.onSetUp(); fixture = new PipelineWithTwoStages(materialRepository, transactionTemplate); fixture.usingConfigHelper(configHelper).usingDbHelper(dbHelper).onSetUp(); configHelper.addPipeline(PIPELINE_NAME, STAGE_NAME); stage = dbHelper.saveBuildingStage(PIPELINE_NAME, STAGE_NAME); } @After public void teardown() throws Exception { fixture.onTearDown(); dbHelper.onTearDown(); configHelper.onTearDown(); } @Test public void rescheduleBuildShouldUpdateCache() throws Exception { final JobInstance hungJob = stage.getJobInstances().get(0); final Pipeline pipeline = dbHelper.getPipelineDao().mostRecentPipeline(PIPELINE_NAME); //Need to do this in transaction because of caching dbHelper.txTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { jobInstanceService.save(new StageIdentifier(pipeline.getName(), -2, pipeline.getLabel(), stage.getName(), String.valueOf(stage.getCounter())), stage.getId(), hungJob); } }); scheduleService.rescheduleJob(hungJob); assertThat(jobStatusCache.currentJob(hungJob.getIdentifier().jobConfigIdentifier()).getState(), is(JobState.Scheduled)); } @Test public void rescheduleBuildShouldNotRescheduleIfReloadedJobIsCompleted() throws Exception { final JobInstance hungJob = stage.getJobInstances().get(0); hungJob.changeState(JobState.Completed, new Date()); //Need to do this in transaction because of caching dbHelper.txTemplate().execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { jobInstanceService.save(new StageIdentifier(PIPELINE_NAME, -2, hungJob.getIdentifier().getPipelineLabel(), stage.getName(), String.valueOf(stage.getCounter())), stage.getId(), hungJob); } }); assertThat(hungJob.isCompleted(), is(true)); assertThat(hungJob.isIgnored(), is(false)); scheduleService.rescheduleJob(hungJob); assertThat(jobInstanceService.buildById(hungJob.getId()).isIgnored(), is(false)); } @Test public void rescheduleHungBuildShouldScheduleNewBuild() throws Exception { JobInstance hungJob = stage.getJobInstances().get(0); dbHelper.getBuildInstanceDao().save(stage.getId(), hungJob); scheduleService.rescheduleJob(hungJob); JobInstance reloaded = dbHelper.getBuildInstanceDao().buildByIdWithTransitions(hungJob.getId()); assertThat(reloaded.isIgnored(), is(true)); assertThat(reloaded.getState(), is(JobState.Rescheduled)); JobPlan newPlan = dbHelper.getBuildInstanceDao().orderedScheduledBuilds().get(0); assertThat(newPlan.getJobId(), is(not(hungJob.getId()))); assertThat(newPlan.getStageName(), is(hungJob.getStageName())); JobInstance newJob = dbHelper.getBuildInstanceDao().buildByIdWithTransitions(newPlan.getJobId()); assertThat(newJob.getState(), is(JobState.Scheduled)); } @Test public void shouldRescheduleBuildAlongWithAssociatedEntitiesCorrectly() throws Exception { dbHelper.cancelStage(stage); Resources resources = new Resources(new Resource("r1"), new Resource("r2")); ArtifactPlans artifactPlans = new ArtifactPlans(Arrays.asList(new ArtifactPlan("s1", "d1"), new ArtifactPlan("s2", "d2"))); ArtifactPropertiesGenerators artifactPropertiesGenerators = new ArtifactPropertiesGenerators(new ArtifactPropertiesGenerator("n1", "s1", "x1"), new ArtifactPropertiesGenerator("n2", "s2", "x2")); configHelper.addAssociatedEntitiesForAJob(PIPELINE_NAME, STAGE_NAME, JOB_NAME, resources, artifactPlans, artifactPropertiesGenerators); dbHelper.schedulePipeline(configHelper.currentConfig().getPipelineConfigByName(new CaseInsensitiveString(PIPELINE_NAME)), new TimeProvider()); JobPlan oldJobPlan = dbHelper.getBuildInstanceDao().orderedScheduledBuilds().get(0); assertThat(oldJobPlan.getResources().size(), is(2)); assertThat(oldJobPlan.getArtifactPlans().size(), is(2)); assertThat(oldJobPlan.getPropertyGenerators().size(), is(2)); JobInstance oldJobInstance = dbHelper.getBuildInstanceDao().buildById(oldJobPlan.getJobId()); scheduleService.rescheduleJob(oldJobInstance); JobInstance reloadedOldJobInstance = dbHelper.getBuildInstanceDao().buildById(oldJobInstance.getId()); assertThat(reloadedOldJobInstance.isIgnored(), is(true)); assertThat(reloadedOldJobInstance.getState(), is(JobState.Rescheduled)); JobPlan newJobPlan = dbHelper.getBuildInstanceDao().orderedScheduledBuilds().get(1); assertThat(newJobPlan.getJobId(), is(not(oldJobInstance.getId()))); assertThat(newJobPlan.getResources().size(), is(2)); for (int i = 0; i < newJobPlan.getResources().size(); i++) { Resource newResource = newJobPlan.getResources().get(i); Resource oldResource = oldJobPlan.getResources().get(i); assertThat(newResource.getId(), is(not(oldResource.getId()))); assertThat(newResource.getName(), is(oldResource.getName())); assertThat(ReflectionUtil.getField(newResource, "buildId"), is(newJobPlan.getJobId())); } assertThat(newJobPlan.getArtifactPlans().size(), is(2)); for (int i = 0; i < newJobPlan.getArtifactPlans().size(); i++) { ArtifactPlan newArtifactPlan = newJobPlan.getArtifactPlans().get(i); ArtifactPlan oldArtifactPlan = oldJobPlan.getArtifactPlans().get(i); assertThat(newArtifactPlan.getId(), is(not(oldArtifactPlan.getId()))); assertThat(newArtifactPlan.getArtifactType(), is(oldArtifactPlan.getArtifactType())); assertThat(newArtifactPlan.getSrc(), is(oldArtifactPlan.getSrc())); assertThat(newArtifactPlan.getDest(), is(oldArtifactPlan.getDest())); assertThat(newArtifactPlan.getArtifactType(), is(oldArtifactPlan.getArtifactType())); assertThat(ReflectionUtil.getField(newArtifactPlan, "buildId"), is(newJobPlan.getJobId())); } assertThat(newJobPlan.getPropertyGenerators().size(), is(2)); for (int i = 0; i < newJobPlan.getPropertyGenerators().size(); i++) { ArtifactPropertiesGenerator newArtifactPropertiesGenerator = newJobPlan.getPropertyGenerators().get(i); ArtifactPropertiesGenerator oldArtifactPropertiesGenerator = oldJobPlan.getPropertyGenerators().get(i); assertThat(newArtifactPropertiesGenerator.getId(), is(not(oldArtifactPropertiesGenerator.getId()))); assertThat(newArtifactPropertiesGenerator.getName(), is(oldArtifactPropertiesGenerator.getName())); assertThat(newArtifactPropertiesGenerator.getSrc(), is(oldArtifactPropertiesGenerator.getSrc())); assertThat(newArtifactPropertiesGenerator.getXpath(), is(oldArtifactPropertiesGenerator.getXpath())); assertThat(ReflectionUtil.getField(newArtifactPropertiesGenerator, "jobId"), is(newJobPlan.getJobId())); } JobInstance newJobInstance = dbHelper.getBuildInstanceDao().buildById(newJobPlan.getJobId()); assertThat(newJobInstance.getState(), is(JobState.Scheduled)); } @Test // #2882 public void rescheduleShouldNotDuplicateResourcesEtc() throws Exception { JobInstance job = scheduledJob(); JobPlan oldPlan = loadJobPlan(job); scheduleService.rescheduleJob(job); JobPlan newPlan = dbHelper.getBuildInstanceDao().orderedScheduledBuilds().get(0); assertThat(newPlan.getResources(), is(oldPlan.getResources())); assertThat(newPlan.getPropertyGenerators(), is(oldPlan.getPropertyGenerators())); assertThat(newPlan.getArtifactPlans(), is(oldPlan.getArtifactPlans())); } private JobPlan loadJobPlan(JobInstance job) { return dbHelper.getBuildInstanceDao().loadPlan(job.getId()); } private JobInstance scheduledJob() throws SQLException { JobInstance hungJob = stage.getJobInstances().get(0); return jobInstanceService.buildByIdWithTransitions(hungJob.getId()); } }