/*
* 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.rits.cloning.Cloner;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.config.exceptions.NoSuchEnvironmentException;
import com.thoughtworks.go.config.merge.MergeEnvironmentConfig;
import com.thoughtworks.go.config.remote.RepoConfigOrigin;
import com.thoughtworks.go.domain.DefaultJobPlan;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.domain.JobPlan;
import com.thoughtworks.go.helper.EnvironmentConfigMother;
import com.thoughtworks.go.presentation.environment.EnvironmentPipelineModel;
import com.thoughtworks.go.remote.work.BuildAssignment;
import com.thoughtworks.go.server.domain.Username;
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
import com.thoughtworks.go.util.command.EnvironmentVariableContext;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.*;
import static com.thoughtworks.go.helper.EnvironmentConfigMother.environments;
import static com.thoughtworks.go.helper.PipelineConfigMother.pipelineConfig;
import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
public class EnvironmentConfigServiceTest {
public GoConfigService mockGoConfigService;
public EnvironmentConfigService environmentConfigService;
private BuildAssignment mockBuildAssignment;
private SecurityService securityService;
private AgentService agentService;
private GoConfigDao goConfigDao;
private EntityHashingService entityHashingService;
@Before
public void setUp() throws Exception {
mockGoConfigService = mock(GoConfigService.class);
mockBuildAssignment = mock(BuildAssignment.class);
goConfigDao = mock(GoConfigDao.class);
securityService = mock(SecurityService.class);
agentService = mock(AgentService.class);
entityHashingService = mock(EntityHashingService.class);
environmentConfigService = new EnvironmentConfigService(mockGoConfigService, securityService, entityHashingService);
}
@Test
public void shouldRegisterAsACruiseConfigChangeListener() throws Exception {
environmentConfigService.initialize();
Mockito.verify(mockGoConfigService).register(environmentConfigService);
}
@Test
public void shouldGetEditablePartOfEnvironmentConfig() throws Exception {
String uat = "uat";
BasicEnvironmentConfig local = new BasicEnvironmentConfig(new CaseInsensitiveString("uat"));
local.addEnvironmentVariable("user", "admin");
BasicEnvironmentConfig remote = new BasicEnvironmentConfig(new CaseInsensitiveString("uat"));
remote.addEnvironmentVariable("foo", "bar");
MergeEnvironmentConfig merged = new MergeEnvironmentConfig(local, remote);
EnvironmentsConfig environments = new EnvironmentsConfig();
environments.add(merged);
environmentConfigService.sync(environments);
BasicCruiseConfig cruiseConfig = new BasicCruiseConfig();
BasicEnvironmentConfig env = (BasicEnvironmentConfig) environmentConfigService.named(uat).getLocal();
cruiseConfig.addEnvironment(env);
BasicEnvironmentConfig expectedToEdit = new Cloner().deepClone(env);
when(mockGoConfigService.getConfigForEditing()).thenReturn(cruiseConfig);
assertThat(environmentConfigService.getEnvironmentForEdit(uat), is(expectedToEdit));
}
@Test
public void shouldFilterWhenAgentIsNotInAnEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
List<JobPlan> filtered = environmentConfigService.filterJobsByAgent(jobs("no-env", "uat", "prod"), "no-env-uuid");
assertThat(filtered.size(), is(1));
assertThat(filtered.get(0).getPipelineName(), is("no-env-pipeline"));
}
@Test
public void shouldFilterWhenAgentIsInTheSameEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
List<JobPlan> filtered = environmentConfigService.filterJobsByAgent(jobs("no-env", "uat", "prod"), "uat-agent");
assertThat(filtered.size(), is(1));
assertThat(filtered.get(0).getPipelineName(), is("uat-pipeline"));
}
@Test
public void shouldFilterWhenAgentIsInMultipleEnvironments() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
List<JobPlan> filtered = environmentConfigService.filterJobsByAgent(jobs("no-env", "uat", "prod"), EnvironmentConfigMother.OMNIPRESENT_AGENT);
assertThat(filtered.size(), is(2));
assertThat(filtered.get(0).getPipelineName(), is("uat-pipeline"));
assertThat(filtered.get(1).getPipelineName(), is("prod-pipeline"));
}
@Test
public void shouldFilterWhenAgentIsInAnotherEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
List<JobPlan> filtered = environmentConfigService.filterJobsByAgent(jobs("no-env", "prod"), "uat-agent");
assertThat(filtered.size(), is(0));
}
@Test
public void shouldAddEnvironmentNameAsEnvironmentVariableToBuildAssignment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
Mockito.when(mockBuildAssignment.getPlan()).thenReturn(jobForPipeline("uat-pipeline"));
environmentConfigService.enhanceEnvironmentVariables(mockBuildAssignment);
EnvironmentVariableContext expectedContext = new EnvironmentVariableContext();
expectedContext.setProperty(EnvironmentVariableContext.GO_ENVIRONMENT_NAME, "uat", false);
Mockito.verify(mockBuildAssignment).enhanceEnvironmentVariables(expectedContext);
}
@Test
public void shouldNotAddEnvironmentNameAsEnvironmentVariableWhenNotInEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
Mockito.when(mockBuildAssignment.getPlan()).thenReturn(jobForPipeline("no-env-pipeline"));
environmentConfigService.enhanceEnvironmentVariables(mockBuildAssignment);
Mockito.verify(mockBuildAssignment, never()).enhanceEnvironmentVariables(any(EnvironmentVariableContext.class));
}
@Test
public void shouldFindPipelinesNamesForAGivenEnvironmentName() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
assertThat(environmentConfigService.pipelinesFor(new CaseInsensitiveString("uat")).size(), is(1));
assertThat(environmentConfigService.pipelinesFor(new CaseInsensitiveString("uat")), hasItem(new CaseInsensitiveString("uat-pipeline")));
assertThat(environmentConfigService.pipelinesFor(new CaseInsensitiveString("prod")), hasItem(new CaseInsensitiveString("prod-pipeline")));
}
@Test
public void shouldFindAgentsForPipelineUnderEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
AgentConfig agentUnderEnv = new AgentConfig("uat-agent", "localhost", "127.0.0.1");
AgentConfig omnipresentAgent = new AgentConfig(EnvironmentConfigMother.OMNIPRESENT_AGENT, "localhost", "127.0.0.2");
Mockito.when(mockGoConfigService.agentByUuid("uat-agent")).thenReturn(agentUnderEnv);
Mockito.when(mockGoConfigService.agentByUuid(EnvironmentConfigMother.OMNIPRESENT_AGENT)).thenReturn(omnipresentAgent);
assertThat(environmentConfigService.agentsForPipeline(new CaseInsensitiveString("uat-pipeline")).size(), is(2));
assertThat(environmentConfigService.agentsForPipeline(new CaseInsensitiveString("uat-pipeline")), hasItem(agentUnderEnv));
assertThat(environmentConfigService.agentsForPipeline(new CaseInsensitiveString("uat-pipeline")), hasItem(omnipresentAgent));
}
@Test
public void shouldFindAgentsForPipelineUnderNoEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
AgentConfig noEnvAgent = new AgentConfig("no-env-agent", "localhost", "127.0.0.1");
Agents agents = new Agents();
agents.add(noEnvAgent);
agents.add(new AgentConfig(EnvironmentConfigMother.OMNIPRESENT_AGENT, "localhost", "127.0.0.2"));
Mockito.when(mockGoConfigService.agents()).thenReturn(agents);
assertThat(environmentConfigService.agentsForPipeline(new CaseInsensitiveString("no-env-pipeline")).size(), is(1));
assertThat(environmentConfigService.agentsForPipeline(new CaseInsensitiveString("no-env-pipeline")), hasItem(noEnvAgent));
}
@Test
public void shouldAddEnvironmentVariablesDefinedInAnEnvironment() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
Mockito.when(mockBuildAssignment.getPlan()).thenReturn(jobForPipeline("no-env-pipeline"));
environmentConfigService.enhanceEnvironmentVariables(mockBuildAssignment);
Mockito.verify(mockBuildAssignment, never()).enhanceEnvironmentVariables(any(EnvironmentVariableContext.class));
}
@Test
public void shouldReturnEnvironmentNames() throws Exception {
environmentConfigService.sync(environments("uat", "prod"));
List<CaseInsensitiveString> environmentNames = environmentConfigService.environmentNames();
assertThat(environmentNames.size(), is(2));
assertThat(environmentNames, hasItem(new CaseInsensitiveString("uat")));
assertThat(environmentNames, hasItem(new CaseInsensitiveString("prod")));
}
@Test
public void shouldReturnEnvironmentsForAnAgent() {
environmentConfigService.sync(environments("uat", "prod"));
Set<String> envForUat = environmentConfigService.environmentsFor("uat-agent");
assertThat(envForUat.size(), is(1));
assertThat(envForUat, hasItem("uat"));
Set<String> envForProd = environmentConfigService.environmentsFor("prod-agent");
assertThat(envForProd.size(), is(1));
assertThat(envForProd, hasItem("prod"));
Set<String> envForOmniPresent = environmentConfigService.environmentsFor(EnvironmentConfigMother.OMNIPRESENT_AGENT);
assertThat(envForOmniPresent.size(), is(2));
assertThat(envForOmniPresent, hasItem("prod"));
assertThat(envForOmniPresent, hasItem("prod"));
}
@Test
public void shouldCreateANewEnvironment() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
List<String> selectedAgents = new ArrayList<>();
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
String environmentName = "foo-environment";
when(mockGoConfigService.hasEnvironmentNamed(new CaseInsensitiveString(environmentName))).thenReturn(false);
environmentConfigService.createEnvironment(env(environmentName, new ArrayList<>(), new ArrayList<>(), selectedAgents), user, result);
assertThat(result.isSuccessful(), is(true));
}
@Test
public void shouldCreateANewEnvironmentWithAgentsAndNoPipelines() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
String environmentName = "foo-environment";
when(mockGoConfigService.hasEnvironmentNamed(new CaseInsensitiveString(environmentName))).thenReturn(false);
environmentConfigService.createEnvironment(env(environmentName, new ArrayList<>(), new ArrayList<>(), Arrays.asList(new String[]{"agent-guid-1"})), user, result);
assertThat(result.isSuccessful(), is(true));
BasicEnvironmentConfig envConfig = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
envConfig.addAgent("agent-guid-1");
}
@Test
public void shouldCreateANewEnvironmentWithEnvironmentVariables() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
List<String> selectedAgents = new ArrayList<>();
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
String environmentName = "foo-environment";
when(mockGoConfigService.hasEnvironmentNamed(new CaseInsensitiveString(environmentName))).thenReturn(false);
List<Map<String, String>> environmentVariables = new ArrayList<>();
environmentVariables.addAll(Arrays.asList(envVar("SHELL", "/bin/zsh"), envVar("HOME", "/home/cruise")));
environmentConfigService.createEnvironment(env(environmentName, new ArrayList<>(), environmentVariables, selectedAgents), user, result);
assertThat(result.isSuccessful(), is(true));
BasicEnvironmentConfig expectedConfig = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
expectedConfig.addEnvironmentVariable("SHELL", "/bin/zsh");
expectedConfig.addEnvironmentVariable("HOME", "/home/cruise");
}
private Map<String, String> envVar(String name, String value) {
HashMap<String, String> map = new HashMap<>();
map.put("name", name);
map.put("value", value);
return map;
}
@Test
public void shouldCreateANewEnvironmentWithPipelineSelections() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
String environmentName = "foo-environment";
when(mockGoConfigService.hasEnvironmentNamed(new CaseInsensitiveString(environmentName))).thenReturn(false);
List<String> selectedPipelines = asList("first", "second");
environmentConfigService.createEnvironment(env(environmentName, selectedPipelines, new ArrayList<>(), new ArrayList<>()), user, result);
assertThat(result.isSuccessful(), is(true));
BasicEnvironmentConfig config = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
config.addPipeline(new CaseInsensitiveString("first"));
config.addPipeline(new CaseInsensitiveString("second"));
}
@Test
public void getAllLocalPipelinesForUser_shouldReturnAllPipelinesToWhichAlongWithTheEnvironmentsToWhichTheyBelong() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
Username user = new Username(new CaseInsensitiveString("user"));
when(mockGoConfigService.getAllLocalPipelineConfigs()).thenReturn(asList(pipelineConfig("foo"), pipelineConfig("bar"), pipelineConfig("baz")));
when(securityService.hasViewPermissionForPipeline(user, "foo")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "bar")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "baz")).thenReturn(false);
environmentConfigService.sync(environmentsConfig("foo-env", "foo"));
List<EnvironmentPipelineModel> pipelines = environmentConfigService.getAllLocalPipelinesForUser(user);
assertThat(pipelines.size(), is(2));
assertThat(pipelines, is(asList(new EnvironmentPipelineModel("bar"), new EnvironmentPipelineModel("foo", "foo-env"))));
}
@Test
public void getAllLocalPipelinesForUser_shouldReturnOnlyLocalPipelines() {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
Username user = new Username(new CaseInsensitiveString("user"));
when(mockGoConfigService.getAllLocalPipelineConfigs()).thenReturn(asList(pipelineConfig("foo"), pipelineConfig("bar"), pipelineConfig("baz")));
when(securityService.hasViewPermissionForPipeline(user, "foo")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "bar")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "baz")).thenReturn(false);
environmentConfigService.sync(environmentsConfig("foo-env", "foo"));
List<EnvironmentPipelineModel> pipelines = environmentConfigService.getAllLocalPipelinesForUser(user);
assertThat(pipelines.size(), is(2));
assertThat(pipelines, is(asList(new EnvironmentPipelineModel("bar"), new EnvironmentPipelineModel("foo", "foo-env"))));
}
@Test
public void getAllRemotePipelinesForUserInEnvironment_shouldReturnOnlyRemotelyAssignedPipelinesWhichUserHasPermsToView() throws NoSuchEnvironmentException {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
Username user = new Username(new CaseInsensitiveString("user"));
when(mockGoConfigService.getAllPipelineConfigs()).thenReturn(asList(pipelineConfig("foo"), pipelineConfig("bar"), pipelineConfig("baz")));
when(securityService.hasViewPermissionForPipeline(user, "foo")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "bar")).thenReturn(true);
when(securityService.hasViewPermissionForPipeline(user, "baz")).thenReturn(false);
EnvironmentsConfig environmentConfigs = environmentsConfig("foo-env", "foo");
EnvironmentConfig fooEnv = environmentConfigs.named(new CaseInsensitiveString("foo-env"));
fooEnv.setOrigins(new RepoConfigOrigin());
environmentConfigService.sync(environmentConfigs);
List<EnvironmentPipelineModel> pipelines = environmentConfigService.getAllRemotePipelinesForUserInEnvironment(user, fooEnv);
assertThat(pipelines.size(), is(1));
assertThat(pipelines, is(asList(new EnvironmentPipelineModel("foo", "foo-env"))));
}
@Test
public void shouldReturnEnvironmentConfigForEdit() throws NoSuchEnvironmentException {
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
CruiseConfig config = new BasicCruiseConfig();
BasicEnvironmentConfig env = new BasicEnvironmentConfig(new CaseInsensitiveString("foo"));
env.addPipeline(new CaseInsensitiveString("bar"));
env.addAgent("baz");
env.addEnvironmentVariable("quux", "bang");
EnvironmentsConfig environments = config.getEnvironments();
environments.add(env);
environmentConfigService.sync(environments);
when(mockGoConfigService.getMergedConfigForEditing()).thenReturn(config);
assertThat(environmentConfigService.getMergedEnvironmentforDisplay("foo", result).getConfigElement(), Is.is(env));
assertThat(result.isSuccessful(), is(true));
}
@Test
public void shouldReturnResultWithMessageThatConfigWasMerged_WhenMergingEnvironmentChanges_NewUpdateEnvironmentMethod() {
String environmentName = "env_name";
EnvironmentConfig environmentConfig = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
when(mockGoConfigService.updateConfig(any(UpdateConfigCommand.class))).thenReturn(ConfigSaveState.MERGED);
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
String md5 = "md5";
environmentConfigService.updateEnvironment(environmentConfig.name().toString(), environmentConfig, user, md5, result);
assertTrue(result.isSuccessful());
assertThat(result.toString(), containsString("UPDATE_ENVIRONMENT_SUCCESS"));
assertThat(result.toString(), containsString(environmentName));
}
@Test
public void shouldReturnResultWithMessageThatConfigisUpdated_WhenUpdatingLatestConfiguration_NewUpdateEnvironmentMethod() {
String environmentName = "env_name";
EnvironmentConfig environmentConfig = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
Username user = new Username(new CaseInsensitiveString("user"));
when(securityService.isUserAdmin(user)).thenReturn(true);
when(mockGoConfigService.updateConfig(any(UpdateConfigCommand.class))).thenReturn(ConfigSaveState.UPDATED);
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
String md5 = "md5";
environmentConfigService.updateEnvironment(environmentConfig.name().toString(), environmentConfig, user, md5, result);
assertTrue(result.isSuccessful());
assertThat(result.toString(), containsString("UPDATE_ENVIRONMENT_SUCCESS"));
assertThat(result.toString(), containsString(environmentName));
}
@Test
public void shouldReturnEnvironmentConfig() throws Exception {
String environmentName = "foo-environment";
String pipelineName = "up42";
environmentConfigService.sync(environmentsConfig(environmentName, pipelineName));
EnvironmentConfig expectedEnvironmentConfig = new BasicEnvironmentConfig(new CaseInsensitiveString(environmentName));
expectedEnvironmentConfig.addPipeline(new CaseInsensitiveString(pipelineName));
assertThat(environmentConfigService.getEnvironmentConfig(environmentName), is(expectedEnvironmentConfig));
}
@Test(expected = NoSuchEnvironmentException.class)
public void shouldThrowExceptionWhenEnvironmentIsAbsent() throws Exception {
String environmentName = "foo-environment";
String pipelineName = "up42";
environmentConfigService.sync(environmentsConfig(environmentName, pipelineName));
environmentConfigService.getEnvironmentConfig("invalid-environment-name");
}
private EnvironmentsConfig environmentsConfig(String envName, String pipelineName) {
EnvironmentsConfig environments = new EnvironmentsConfig();
BasicEnvironmentConfig config = new BasicEnvironmentConfig(new CaseInsensitiveString(envName));
config.addPipeline(new CaseInsensitiveString(pipelineName));
environments.add(config);
return environments;
}
private List<JobPlan> jobs(String... envNames) {
ArrayList<JobPlan> plans = new ArrayList<>();
for (String envName : envNames) {
plans.add(jobForPipeline(envName + "-pipeline"));
}
return plans;
}
private DefaultJobPlan jobForPipeline(String pipelineName) {
JobIdentifier jobIdentifier = new JobIdentifier(pipelineName, 1, "1", "defaultStage", "1", "job1", 100L);
return new DefaultJobPlan(new Resources(), new ArtifactPlans(), new ArtifactPropertiesGenerators(), 1L, jobIdentifier, null, new EnvironmentVariablesConfig(), new EnvironmentVariablesConfig(), null);
}
public static BasicEnvironmentConfig env(String name, List<String> selectedPipelines, List<Map<String, String>> environmentVariables, List<String> selectedAgents) {
BasicEnvironmentConfig config = new BasicEnvironmentConfig(new CaseInsensitiveString(name));
for (String selectedPipeline : selectedPipelines) {
config.addPipeline(new CaseInsensitiveString(selectedPipeline));
}
for (String selectedAgent : selectedAgents) {
config.addAgent(selectedAgent);
}
for (Map<String, String> environmentVariable : environmentVariables) {
config.getVariables().add(environmentVariable.get("name"), environmentVariable.get("value"));
}
return config;
}
}