/*
* 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 com.thoughtworks.go.config.AgentConfig;
import com.thoughtworks.go.config.Agents;
import com.thoughtworks.go.config.PipelineConfig;
import com.thoughtworks.go.config.elastic.ElasticProfile;
import com.thoughtworks.go.domain.AgentInstance;
import com.thoughtworks.go.domain.DefaultSchedulingContext;
import com.thoughtworks.go.domain.JobPlan;
import com.thoughtworks.go.domain.SchedulingContext;
import com.thoughtworks.go.helper.AgentMother;
import com.thoughtworks.go.helper.JobConfigMother;
import com.thoughtworks.go.helper.PipelineConfigMother;
import com.thoughtworks.go.server.domain.ElasticAgentMetadata;
import com.thoughtworks.go.server.service.builders.BuilderFactory;
import com.thoughtworks.go.server.transaction.TransactionTemplate;
import com.thoughtworks.go.server.websocket.AgentRemoteHandler;
import com.thoughtworks.go.util.SystemEnvironment;
import com.thoughtworks.go.util.TimeProvider;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
public class BuildAssignmentServiceTest {
@Mock
private GoConfigService goConfigService;
@Mock
private JobInstanceService jobInstanceService;
@Mock
private ScheduleService scheduleService;
@Mock
private ElasticAgentPluginService elasticAgentPluginService;
@Mock
private TimeProvider timeProvider;
@Mock
private AgentRemoteHandler agentRemoteHandler;
@Mock
private BuilderFactory builderFactory;
@Mock
private PipelineService pipelineService;
@Mock
private ScheduledPipelineLoader scheduledPipelineLoader;
@Mock
private EnvironmentConfigService environmentConfigService;
@Mock
private AgentService agentService;
private BuildAssignmentService buildAssignmentService;
@Mock private TransactionTemplate transactionTemplate;
private SchedulingContext schedulingContext;
private ArrayList<JobPlan> jobPlans;
private AgentConfig elasticAgent;
private AgentInstance elasticAgentInstance;
private ElasticProfile elasticProfile1;
private ElasticProfile elasticProfile2;
private String elasticProfileId1;
private String elasticProfileId2;
private AgentInstance regularAgentInstance;
@Before
public void setUp() throws Exception {
initMocks(this);
buildAssignmentService = new BuildAssignmentService(goConfigService, jobInstanceService, scheduleService, agentService, environmentConfigService, transactionTemplate, scheduledPipelineLoader, pipelineService, builderFactory, agentRemoteHandler, elasticAgentPluginService, timeProvider);
elasticProfileId1 = "elastic.profile.id.1";
elasticProfileId2 = "elastic.profile.id.2";
elasticAgent = AgentMother.elasticAgent();
elasticAgentInstance = AgentInstance.createFromConfig(elasticAgent, new SystemEnvironment());
regularAgentInstance = AgentInstance.createFromConfig(AgentMother.approvedAgent(), new SystemEnvironment());
elasticProfile1 = new ElasticProfile(elasticProfileId1, elasticAgent.getElasticPluginId());
elasticProfile2 = new ElasticProfile(elasticProfileId2, elasticAgent.getElasticPluginId());
jobPlans = new ArrayList<>();
HashMap<String, ElasticProfile> profiles = new HashMap<>();
profiles.put(elasticProfile1.getId(), elasticProfile1);
profiles.put(elasticProfile2.getId(), elasticProfile2);
schedulingContext = new DefaultSchedulingContext("me", new Agents(elasticAgent), profiles);
when(jobInstanceService.orderedScheduledBuilds()).thenReturn(jobPlans);
when(environmentConfigService.filterJobsByAgent(Matchers.eq(jobPlans), Matchers.any(String.class))).thenReturn(jobPlans);
when(environmentConfigService.envForPipeline(Matchers.any(String.class))).thenReturn("");
}
@Test
public void shouldMatchAnElasticJobToAnElasticAgentOnlyIfThePluginAgreesToTheAssignment() {
PipelineConfig pipelineWithElasticJob = PipelineConfigMother.pipelineWithElasticJob(elasticProfileId1);
JobPlan jobPlan = new InstanceFactory().createJobPlan(pipelineWithElasticJob.first().getJobs().first(), schedulingContext);
jobPlans.add(jobPlan);
when(elasticAgentPluginService.shouldAssignWork(elasticAgentInstance.elasticAgentMetadata(), "", jobPlan.getElasticProfile())).thenReturn(true);
buildAssignmentService.onTimer();
JobPlan matchingJob = buildAssignmentService.findMatchingJob(elasticAgentInstance);
assertThat(matchingJob, is(jobPlan));
assertThat(buildAssignmentService.jobPlans().size(), is(0));
}
@Test
public void shouldNotMatchAnElasticJobToAnElasticAgentOnlyIfThePluginIdMatches() {
PipelineConfig pipelineWithElasticJob = PipelineConfigMother.pipelineWithElasticJob(elasticProfileId1);
JobPlan jobPlan1 = new InstanceFactory().createJobPlan(pipelineWithElasticJob.first().getJobs().first(), schedulingContext);
jobPlans.add(jobPlan1);
when(elasticAgentPluginService.shouldAssignWork(elasticAgentInstance.elasticAgentMetadata(), "", jobPlan1.getElasticProfile())).thenReturn(false);
buildAssignmentService.onTimer();
JobPlan matchingJob = buildAssignmentService.findMatchingJob(elasticAgentInstance);
assertThat(matchingJob, is(nullValue()));
assertThat(buildAssignmentService.jobPlans().size(), is(1));
}
@Test
public void shouldMatchAnElasticJobToAnElasticAgentOnlyIfThePluginAgreesToTheAssignmentWhenMultipleElasticJobsRequiringTheSamePluginAreScheduled() {
PipelineConfig pipelineWith2ElasticJobs = PipelineConfigMother.pipelineWithElasticJob(elasticProfileId1, elasticProfileId2);
JobPlan jobPlan1 = new InstanceFactory().createJobPlan(pipelineWith2ElasticJobs.first().getJobs().first(), schedulingContext);
JobPlan jobPlan2 = new InstanceFactory().createJobPlan(pipelineWith2ElasticJobs.first().getJobs().last(), schedulingContext);
jobPlans.add(jobPlan1);
jobPlans.add(jobPlan2);
when(elasticAgentPluginService.shouldAssignWork(elasticAgentInstance.elasticAgentMetadata(), "", jobPlan1.getElasticProfile())).thenReturn(false);
when(elasticAgentPluginService.shouldAssignWork(elasticAgentInstance.elasticAgentMetadata(), "", jobPlan2.getElasticProfile())).thenReturn(true);
buildAssignmentService.onTimer();
JobPlan matchingJob = buildAssignmentService.findMatchingJob(elasticAgentInstance);
assertThat(matchingJob, is(jobPlan2));
assertThat(buildAssignmentService.jobPlans().size(), is(1));
}
@Test
public void shouldMatchNonElasticJobToNonElasticAgentIfResourcesMatch(){
PipelineConfig pipeline = PipelineConfigMother.pipelineConfig(UUID.randomUUID().toString());
pipeline.first().getJobs().add(JobConfigMother.jobWithNoResourceRequirement());
pipeline.first().getJobs().add(JobConfigMother.elasticJob(elasticProfileId1));
JobPlan elasticJobPlan = new InstanceFactory().createJobPlan(pipeline.first().getJobs().last(), schedulingContext);
JobPlan regularJobPlan = new InstanceFactory().createJobPlan(pipeline.first().getJobs().first(), schedulingContext);
jobPlans.add(elasticJobPlan);
jobPlans.add(regularJobPlan);
buildAssignmentService.onTimer();
JobPlan matchingJob = buildAssignmentService.findMatchingJob(regularAgentInstance);
assertThat(matchingJob, is(regularJobPlan));
assertThat(buildAssignmentService.jobPlans().size(), is(1));
verify(elasticAgentPluginService, never()).shouldAssignWork(Matchers.any(ElasticAgentMetadata.class), Matchers.any(String.class), Matchers.any(ElasticProfile.class));
}
}