/* * 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.scheduling; import com.thoughtworks.go.config.CaseInsensitiveString; import com.thoughtworks.go.config.PipelineConfig; import com.thoughtworks.go.config.materials.Materials; import com.thoughtworks.go.domain.DefaultSchedulingContext; import com.thoughtworks.go.domain.Pipeline; import com.thoughtworks.go.domain.SchedulingContext; import com.thoughtworks.go.domain.buildcause.BuildCause; import com.thoughtworks.go.domain.materials.Material; import com.thoughtworks.go.helper.ScheduleCheckMatcher; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.materials.MaterialDatabaseUpdater; import com.thoughtworks.go.server.messaging.StubScheduleCheckCompletedListener; import com.thoughtworks.go.server.persistence.MaterialRepository; import com.thoughtworks.go.server.service.*; import com.thoughtworks.go.server.service.result.ServerHealthStateOperationResult; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.serverhealth.ServerHealthState; import com.thoughtworks.go.util.TimeProvider; import com.thoughtworks.go.utils.Timeout; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import java.util.HashMap; import java.util.Map; import static com.thoughtworks.go.utils.Assertions.assertAlwaysHappens; import static com.thoughtworks.go.utils.Assertions.assertWillHappen; import static com.thoughtworks.go.utils.Timeout.TEN_SECONDS; import static junit.framework.TestCase.assertNull; import static org.junit.Assert.fail; @Component public class ScheduleHelper { private StubScheduleCheckCompletedListener scheduleCompleteListener; private PipelineScheduler pipelineScheduler; private final GoConfigService goConfigService; private PipelineService pipelineService; private MaterialRepository materialRepository; private final PipelineScheduleQueue pipelineScheduleQueue; private final TransactionTemplate transactionTemplate; static private long waitTime; private static final long ONE_SECOND = 1000; private MaterialDatabaseUpdater materialDatabaseUpdater; private InstanceFactory instanceFactory; @Autowired public ScheduleHelper(ScheduleCheckCompletedTopic scheduleCheckCompletedTopic, PipelineScheduler pipelineScheduler, GoConfigService goConfigService, MaterialDatabaseUpdater materialDatabaseUpdater, PipelineService pipelineService, MaterialRepository materialRepository, PipelineScheduleQueue pipelineScheduleQueue, TransactionTemplate transactionTemplate, InstanceFactory instanceFactory) { this.pipelineScheduler = pipelineScheduler; this.goConfigService = goConfigService; this.materialDatabaseUpdater = materialDatabaseUpdater; this.pipelineService = pipelineService; this.materialRepository = materialRepository; this.pipelineScheduleQueue = pipelineScheduleQueue; this.transactionTemplate = transactionTemplate; this.instanceFactory = instanceFactory; this.scheduleCompleteListener = new StubScheduleCheckCompletedListener(); scheduleCheckCompletedTopic.addListener(scheduleCompleteListener); } public Pipeline schedule(final PipelineConfig pipelineConfig, final BuildCause buildCause, final String approvedBy) { return schedule(pipelineConfig, buildCause, approvedBy, new DefaultSchedulingContext(approvedBy)); } public Pipeline schedule(final PipelineConfig pipelineConfig, final BuildCause buildCause, final String approvedBy, final SchedulingContext schedulingContext) { return (Pipeline) transactionTemplate.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { materialRepository.save(buildCause.getMaterialRevisions()); Pipeline pipeline = instanceFactory.createPipelineInstance(pipelineConfig, buildCause, schedulingContext, "md5-test", new TimeProvider()); pipeline = pipelineService.save(pipeline); return pipeline; } }); } public ServerHealthState manuallySchedulePipelineWithRealMaterials(String pipeline, Username username) throws Exception { final HashMap<String, String> revisions = new HashMap<>(); return manuallySchedulePipelineWithRealMaterials(pipeline, username, revisions); } public ServerHealthState manuallySchedulePipelineWithRealMaterials(String pipeline, Username username, Map<String, String> pegging) throws Exception { updateMaterials(pipeline); ServerHealthStateOperationResult result = new ServerHealthStateOperationResult(); final HashMap<String, String> environmentVariables = new HashMap<>(); HashMap<String, String> secureEnvironmentVariables = new HashMap<>(); pipelineScheduler.manualProduceBuildCauseAndSave(pipeline, username, new ScheduleOptions(pegging, environmentVariables, secureEnvironmentVariables), result); if (result.canContinue()) { waitForAnyScheduled(30); } return result.getServerHealthState(); } public Map<String, BuildCause> waitForAnyScheduled(int seconds) { int count = 0; Map<String, BuildCause> afterLoad = pipelineScheduleQueue.toBeScheduled(); while (afterLoad.isEmpty()) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } afterLoad = pipelineScheduleQueue.toBeScheduled(); if (count++ > seconds) { fail("Never scheduled"); } } return afterLoad; } public void waitForNotScheduled(int seconds,String pipelineName) { int count = 0; Map<String, BuildCause> afterLoad = pipelineScheduleQueue.toBeScheduled(); while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } afterLoad = pipelineScheduleQueue.toBeScheduled(); if (count++ > seconds) { return ; } BuildCause cause = afterLoad.get(pipelineName); assertNull(cause); } } public void autoSchedulePipelinesWithRealMaterials(String... pipelines) throws Exception { updateMaterials(pipelines); long startTime = System.currentTimeMillis(); pipelineScheduler.onTimer(); if (pipelines.length == 0) { assertAlwaysHappens(pipelines, ScheduleCheckMatcher.scheduleCheckCompleted(scheduleCompleteListener), waitTime()); } else { assertWillHappen(pipelines, ScheduleCheckMatcher.scheduleCheckCompleted(scheduleCompleteListener), Timeout.TWENTY_SECONDS); long endTime = System.currentTimeMillis(); setWaitTime(endTime - startTime); } scheduleCompleteListener.reset(); } private void updateMaterials(String... pipelines) throws Exception { for (String pipeline : pipelines) { Materials materials = new MaterialConfigConverter().toMaterials(goConfigService.getCurrentConfig().pipelineConfigByName(new CaseInsensitiveString(pipeline)).materialConfigs()); for (Material material : materials) { materialDatabaseUpdater.updateMaterial(material); } } } private void setWaitTime(long time) { if(time > waitTime){ waitTime = time; } } private long waitTime() { long result; if (waitTime > 0) { result = (waitTime > ONE_SECOND? waitTime:ONE_SECOND) * 2; } else { result = TEN_SECONDS.inMillis(); } return result; } }