@Test public void shouldScheduleIfAgentMatchingResources() throws Exception { JobConfig plan = evolveConfig .findBy(new CaseInsensitiveString(STAGE_NAME)) .jobConfigByInstanceName("unit", true); plan.addResource("some-resource"); scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY); AgentConfig agentConfig = AgentMother.localAgent(); agentConfig.addResource(new Resource("some-resource")); buildAssignmentService.onTimer(); Work work = buildAssignmentService.assignWorkToAgent(agent(agentConfig)); assertThat(work, is(not((Work) BuildAssignmentService.NO_WORK))); Pipeline pipeline = pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name())); JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit"); JobPlan loadedPlan = jobInstanceDao.loadPlan(job.getId()); assertThat(loadedPlan.getResources(), is((List<Resource>) plan.resources())); assertThat(job.getState(), is(JobState.Assigned)); assertThat(job.getAgentUuid(), is(agentConfig.getUuid())); }
@Test public void shouldCancelOutOfDateBuilds() throws Exception { fixture.createPipelineWithFirstStageScheduled(); buildAssignmentService.onTimer(); configHelper.removeStage(fixture.pipelineName, fixture.devStage); buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig()); Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); JobInstance job = pipeline.getFirstStage().getJobInstances().first(); assertThat(job.getState(), is(JobState.Completed)); assertThat(job.getResult(), is(JobResult.Cancelled)); }
@Test public void shouldCancelBuildBelongingToNonExistentPipelineWhenCreatingWork() throws Exception { fixture.createPipelineWithFirstStageScheduled(); Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); ScheduledPipelineLoader scheduledPipelineLoader = mock(ScheduledPipelineLoader.class); when(scheduledPipelineLoader.pipelineWithPasswordAwareBuildCauseByBuildId( pipeline.getFirstStage().getJobInstances().first().getId())) .thenThrow(new PipelineNotFoundException("thrown by mockPipelineService")); GoConfigService mockGoConfigService = mock(GoConfigService.class); CruiseConfig config = configHelper.currentConfig(); configHelper.removePipeline(fixture.pipelineName, config); when(mockGoConfigService.getCurrentConfig()).thenReturn(config); buildAssignmentService = new BuildAssignmentService( mockGoConfigService, jobInstanceService, scheduleService, agentService, environmentConfigService, timeProvider, transactionTemplate, scheduledPipelineLoader, pipelineService, builderFactory, agentRemoteHandler); buildAssignmentService.onTimer(); AgentConfig agentConfig = AgentMother.localAgent(); agentConfig.addResource(new Resource("some-other-resource")); try { buildAssignmentService.assignWorkToAgent(agent(agentConfig)); fail("should have thrown PipelineNotFoundException"); } catch (PipelineNotFoundException e) { // ok } pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); JobInstance job = pipeline.getFirstStage().getJobInstances().first(); assertThat(job.getState(), is(JobState.Completed)); assertThat(job.getResult(), is(JobResult.Cancelled)); Stage stage = stageDao.findStageWithIdentifier(job.getIdentifier().getStageIdentifier()); assertThat(stage.getState(), is(StageState.Cancelled)); assertThat(stage.getResult(), is(StageResult.Cancelled)); }
@Test public void shouldNotScheduleIfAgentDoesNotHaveResources() throws Exception { JobConfig plan = evolveConfig .findBy(new CaseInsensitiveString(STAGE_NAME)) .jobConfigByInstanceName("unit", true); plan.addResource("some-resource"); scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY); Work work = buildAssignmentService.assignWorkToAgent(agent(AgentMother.localAgent())); Pipeline pipeline = pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name())); JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit"); assertThat(work, is((Work) BuildAssignmentService.NO_WORK)); assertThat(job.getState(), is(JobState.Scheduled)); assertThat(job.getAgentUuid(), is(nullValue())); }
@Test public void shouldNotAssignCancelledJob() throws Exception { AgentIdentifier instance = agent(AgentMother.localAgent()); Pipeline pipeline = instanceFactory.createPipelineInstance( evolveConfig, modifyNoFiles(evolveConfig), new DefaultSchedulingContext(DEFAULT_APPROVED_BY), md5, new TimeProvider()); dbHelper.savePipelineWithStagesAndMaterials(pipeline); buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig()); JobInstance job = buildOf(pipeline); job.cancel(); jobInstanceDao.updateStateAndResult(job); assertThat( buildAssignmentService.assignWorkToAgent(instance), is((Work) BuildAssignmentService.NO_WORK)); }
@Test public void shouldCancelBuildBelongingToNonExistentPipeline() throws Exception { fixture.createPipelineWithFirstStageScheduled(); buildAssignmentService.onTimer(); configHelper.removePipeline(fixture.pipelineName); AgentConfig agentConfig = AgentMother.localAgent(); agentConfig.addResource(new Resource("some-other-resource")); assertThat( (NoWork) buildAssignmentService.assignWorkToAgent(agent(agentConfig)), Matchers.is(BuildAssignmentService.NO_WORK)); Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); JobInstance job = pipeline.getFirstStage().getJobInstances().first(); assertThat(job.getState(), is(JobState.Completed)); assertThat(job.getResult(), is(JobResult.Cancelled)); Stage stage = stageDao.findStageWithIdentifier(job.getIdentifier().getStageIdentifier()); assertThat(stage.getState(), is(StageState.Cancelled)); assertThat(stage.getResult(), is(StageResult.Cancelled)); }
@Test public void shouldCancelBuildsForDeletedJobsWhenPipelineConfigChanges() throws Exception { fixture = new PipelineWithTwoStages(materialRepository, transactionTemplate).usingTwoJobs(); fixture.usingConfigHelper(configHelper).usingDbHelper(dbHelper).onSetUp(); fixture.createPipelineWithFirstStageScheduled(); buildAssignmentService.onTimer(); configHelper.removeJob(fixture.pipelineName, fixture.devStage, fixture.JOB_FOR_DEV_STAGE); buildAssignmentService.onPipelineConfigChange( goConfigService .getCurrentConfig() .getPipelineConfigByName(new CaseInsensitiveString(fixture.pipelineName)), "g1"); Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); JobInstance deletedJob = pipeline.getFirstStage().getJobInstances().getByName(fixture.JOB_FOR_DEV_STAGE); assertThat(deletedJob.getState(), is(JobState.Completed)); assertThat(deletedJob.getResult(), is(JobResult.Cancelled)); JobInstance retainedJob = pipeline.getFirstStage().getJobInstances().getByName(fixture.DEV_STAGE_SECOND_JOB); assertThat(retainedJob.getState(), is(JobState.Scheduled)); assertThat(retainedJob.getResult(), is(JobResult.Unknown)); }
@Test public void shouldRescheduleAbandonedBuild() throws SQLException { AgentIdentifier instance = agent(AgentMother.localAgent()); Pipeline pipeline = instanceFactory.createPipelineInstance( evolveConfig, modifyNoFiles(evolveConfig), new DefaultSchedulingContext(DEFAULT_APPROVED_BY), md5, new TimeProvider()); dbHelper.savePipelineWithStagesAndMaterials(pipeline); buildAssignmentService.onConfigChange(goConfigService.getCurrentConfig()); buildAssignmentService.onTimer(); buildAssignmentService.assignWorkToAgent(instance); long firstAssignedBuildId = buildOf(pipeline).getId(); // somehow agent abandoned its original build... buildAssignmentService.assignWorkToAgent(instance); JobInstance reloaded = jobInstanceDao.buildByIdWithTransitions(firstAssignedBuildId); assertThat(reloaded.getState(), is(JobState.Rescheduled)); assertThat(reloaded.isIgnored(), is(true)); }
@Test public void shouldReScheduleToCorrectAgent() throws Exception { JobConfig plan = evolveConfig .findBy(new CaseInsensitiveString(STAGE_NAME)) .jobConfigByInstanceName("unit", true); plan.addResource("some-resource"); scheduleHelper.schedule(evolveConfig, modifySomeFiles(evolveConfig), DEFAULT_APPROVED_BY); buildAssignmentService.onTimer(); AgentConfig agentConfig = AgentMother.localAgent(); agentConfig.addResource(new Resource("some-resource")); Work work = buildAssignmentService.assignWorkToAgent(agent(agentConfig)); assertThat(work, is(not((Work) BuildAssignmentService.NO_WORK))); Pipeline pipeline = pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name())); JobInstance job = pipeline.findStage(STAGE_NAME).findJob("unit"); JobInstance runningJob = jobInstanceDao.buildByIdWithTransitions(job.getId()); scheduleService.rescheduleJob(runningJob); pipeline = pipelineDao.mostRecentPipeline(CaseInsensitiveString.str(evolveConfig.name())); JobInstance rescheduledJob = pipeline.findStage(STAGE_NAME).findJob("unit"); assertThat(rescheduledJob.getId(), not(runningJob.getId())); buildAssignmentService.onTimer(); Work noResourcesWork = buildAssignmentService.assignWorkToAgent( agent(AgentMother.localAgentWithResources("WITHOUT_RESOURCES"))); assertThat(noResourcesWork, is((Work) BuildAssignmentService.NO_WORK)); buildAssignmentService.onTimer(); Work correctAgentWork = buildAssignmentService.assignWorkToAgent(agent(agentConfig)); assertThat(correctAgentWork, is(not((Work) BuildAssignmentService.NO_WORK))); }
@Test public void shouldNotReloadScheduledJobPlansWhenAgentWorkAssignmentIsInProgress() throws Exception { fixture.createPipelineWithFirstStageScheduled(); Pipeline pipeline = pipelineDao.mostRecentPipeline(fixture.pipelineName); JobInstance job = pipeline.getFirstStage().getJobInstances().first(); final JobInstanceService mockJobInstanceService = mock(JobInstanceService.class); final Pipeline pipeline1 = pipeline; final Semaphore sem = new Semaphore(1); sem.acquire(); when(mockJobInstanceService.orderedScheduledBuilds()) .thenReturn(jobInstanceService.orderedScheduledBuilds()); when(mockJobInstanceService.buildByIdWithTransitions(job.getId())) .thenReturn(jobInstanceService.buildByIdWithTransitions(job.getId())); ScheduledPipelineLoader scheduledPipelineLoader = new ScheduledPipelineLoader(null, null, null, null, null, null, null, null) { @Override public Pipeline pipelineWithPasswordAwareBuildCauseByBuildId(long buildId) { sem.release(); sleepQuietly(1000); verify(mockJobInstanceService, times(1)).orderedScheduledBuilds(); return pipeline1; } }; final BuildAssignmentService buildAssignmentServiceUnderTest = new BuildAssignmentService( goConfigService, mockJobInstanceService, scheduleService, agentService, environmentConfigService, timeProvider, transactionTemplate, scheduledPipelineLoader, pipelineService, builderFactory, agentRemoteHandler); final Throwable[] fromThread = new Throwable[1]; buildAssignmentServiceUnderTest.onTimer(); Thread assigner = new Thread( new Runnable() { public void run() { try { final AgentConfig agentConfig = AgentMother.localAgentWithResources("some-other-resource"); buildAssignmentServiceUnderTest.assignWorkToAgent(agent(agentConfig)); } catch (Throwable e) { e.printStackTrace(); fromThread[0] = e; } finally { } } }, "assignmentThread"); assigner.start(); sem.acquire(); buildAssignmentServiceUnderTest.onTimer(); assigner.join(); assertThat(fromThread[0], is(nullValue())); }
@Test public void shouldSetAServerHealthMessageWhenMaterialForPipelineWithBuildCauseIsNotFound() throws IllegalArtifactLocationException, IOException { PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfig( "last", new StageConfig( new CaseInsensitiveString("stage"), new JobConfigs(new JobConfig("job-one")))); pipelineConfig.materialConfigs().clear(); SvnMaterialConfig onDirOne = MaterialConfigsMother.svnMaterialConfig( "google.com", "dirOne", "loser", "boozer", false, "**/*.html"); final P4MaterialConfig onDirTwo = MaterialConfigsMother.p4MaterialConfig( "host:987654321", "zoozer", "secret", "through-the-window", true); onDirTwo.setConfigAttributes(Collections.singletonMap(ScmMaterialConfig.FOLDER, "dirTwo")); pipelineConfig.addMaterialConfig(onDirOne); pipelineConfig.addMaterialConfig(onDirTwo); configHelper.addPipeline(pipelineConfig); Pipeline building = PipelineMother.building(pipelineConfig); final Pipeline pipeline = dbHelper.savePipelineWithMaterials(building); CruiseConfig cruiseConfig = configHelper.currentConfig(); PipelineConfig cfg = cruiseConfig.pipelineConfigByName(new CaseInsensitiveString("last")); cfg.removeMaterialConfig(cfg.materialConfigs().get(1)); configHelper.writeConfigFile(cruiseConfig); assertThat( serverHealthService.filterByScope(HealthStateScope.forPipeline("last")).size(), is(0)); final long jobId = pipeline.getStages().get(0).getJobInstances().get(0).getId(); Date currentTime = new Date(System.currentTimeMillis() - 1); Pipeline loadedPipeline = (Pipeline) transactionTemplate.execute( new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { Pipeline loadedPipeline = null; try { loadedPipeline = loader.pipelineWithPasswordAwareBuildCauseByBuildId(jobId); fail( "should not have loaded pipeline with build-cause as one of the necessary materials was not found"); } catch (Exception e) { assertThat(e, is(instanceOf(StaleMaterialsOnBuildCause.class))); assertThat( e.getMessage(), is( "Cannot load job 'last/" + pipeline.getCounter() + "/stage/1/job-one' because material " + onDirTwo + " was not found in config.")); } return loadedPipeline; } }); assertThat(loadedPipeline, is(nullValue())); JobInstance reloadedJobInstance = jobInstanceService.buildById(jobId); assertThat(reloadedJobInstance.getState(), is(JobState.Completed)); assertThat(reloadedJobInstance.getResult(), is(JobResult.Failed)); assertThat( serverHealthService .filterByScope(HealthStateScope.forJob("last", "stage", "job-one")) .size(), is(1)); ServerHealthState error = serverHealthService .filterByScope(HealthStateScope.forJob("last", "stage", "job-one")) .get(0); assertThat( error, is( ServerHealthState.error( "Cannot load job 'last/" + pipeline.getCounter() + "/stage/1/job-one' because material " + onDirTwo + " was not found in config.", "Job for pipeline 'last/" + pipeline.getCounter() + "/stage/1/job-one' has been failed as one or more material configurations were either changed or removed.", HealthStateType.general(HealthStateScope.forJob("last", "stage", "job-one"))))); DateTime expiryTime = (DateTime) ReflectionUtil.getField(error, "expiryTime"); assertThat(expiryTime.toDate().after(currentTime), is(true)); assertThat( expiryTime.toDate().before(new Date(System.currentTimeMillis() + 5 * 60 * 1000 + 1)), is(true)); String logText = FileUtil.readToEnd(consoleService.findConsoleArtifact(reloadedJobInstance.getIdentifier())); assertThat( logText, containsString( "Cannot load job 'last/" + pipeline.getCounter() + "/stage/1/job-one' because material " + onDirTwo + " was not found in config.")); assertThat( logText, containsString( "Job for pipeline 'last/" + pipeline.getCounter() + "/stage/1/job-one' has been failed as one or more material configurations were either changed or removed.")); }