/* * 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.config; import java.util.Arrays; import java.util.Map; import com.thoughtworks.go.config.materials.MaterialConfigs; import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig; import com.thoughtworks.go.domain.materials.MaterialConfig; import com.thoughtworks.go.helper.*; import com.thoughtworks.go.util.DataStructureUtils; import org.hamcrest.Matchers; import org.junit.Test; import static com.thoughtworks.go.util.DataStructureUtils.a; import static com.thoughtworks.go.util.DataStructureUtils.m; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertThat; public class PipelineTemplateConfigTest { @Test public void shouldFindByName() { PipelineTemplateConfig pipelineTemplateConfig = new PipelineTemplateConfig(new CaseInsensitiveString("pipeline"), StageConfigMother.manualStage("manual"), StageConfigMother.manualStage("manual2")); assertThat(pipelineTemplateConfig.findBy(new CaseInsensitiveString("manuaL2")).name(), is(new CaseInsensitiveString("manual2"))); } @Test public void shouldGetStageByName() { PipelineTemplateConfig pipelineTemplateConfig = new PipelineTemplateConfig(new CaseInsensitiveString("pipeline"), StageConfigMother.manualStage("manual"), StageConfigMother.manualStage("manual2")); assertThat(pipelineTemplateConfig.findBy(new CaseInsensitiveString("manual")).name(), is(new CaseInsensitiveString("manual"))); assertThat(pipelineTemplateConfig.findBy(new CaseInsensitiveString("Does-not-exist")), is(nullValue())); } @Test public void shouldIgnoreCaseWhileMatchingATemplateWithName() { assertThat(new PipelineTemplateConfig(new CaseInsensitiveString("FOO")).matches(new CaseInsensitiveString("foo")), is(true)); assertThat(new PipelineTemplateConfig(new CaseInsensitiveString("FOO")).matches(new CaseInsensitiveString("FOO")), is(true)); assertThat(new PipelineTemplateConfig(new CaseInsensitiveString("FOO")).matches(new CaseInsensitiveString("bar")), is(false)); } @Test public void shouldSetPrimitiveAttributes() { PipelineTemplateConfig pipelineTemplateConfig = new PipelineTemplateConfig(); Map map = m(PipelineTemplateConfig.NAME, "templateName"); pipelineTemplateConfig.setConfigAttributes(map); assertThat(pipelineTemplateConfig.name(), is(new CaseInsensitiveString("templateName"))); } @Test public void shouldUpdateAuthorization() { PipelineTemplateConfig templateConfig = PipelineTemplateConfigMother.createTemplate("template-1"); templateConfig.setConfigAttributes(m(BasicPipelineConfigs.AUTHORIZATION, a( DataStructureUtils.m(Authorization.NAME, "loser", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, "boozer", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, "geezer", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString())))))); Authorization authorization = templateConfig.getAuthorization(); assertThat(authorization.getAdminsConfig().size(), Matchers.is(3)); assertThat(authorization.getAdminsConfig(), hasItem(new AdminUser(new CaseInsensitiveString("loser")))); assertThat(authorization.getAdminsConfig(), hasItem(new AdminUser(new CaseInsensitiveString("boozer")))); assertThat(authorization.getAdminsConfig(), hasItem(new AdminUser(new CaseInsensitiveString("geezer")))); assertThat(authorization.getOperationConfig().size(), Matchers.is(0)); assertThat(authorization.getViewConfig().size(), Matchers.is(0)); } @Test public void shouldReInitializeAuthorizationIfWeClearAllPermissions() { PipelineTemplateConfig templateConfig = PipelineTemplateConfigMother.createTemplate("template-1"); templateConfig.setConfigAttributes(m(BasicPipelineConfigs.AUTHORIZATION, a( DataStructureUtils.m(Authorization.NAME, "loser", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, "boozer", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, "geezer", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString())))))); Authorization authorization = templateConfig.getAuthorization(); assertThat(authorization.getAdminsConfig().size(), Matchers.is(3)); templateConfig.setConfigAttributes(m()); authorization = templateConfig.getAuthorization(); assertThat(authorization.getAdminsConfig().size(), Matchers.is(0)); } @Test public void shouldIgnoreBlankUserWhileSettingAttributes() { PipelineTemplateConfig templateConfig = PipelineTemplateConfigMother.createTemplate("template-1"); templateConfig.setConfigAttributes(m(BasicPipelineConfigs.AUTHORIZATION, a( DataStructureUtils.m(Authorization.NAME, "", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, null, Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString()))), DataStructureUtils.m(Authorization.NAME, "geezer", Authorization.TYPE, Authorization.UserType.USER.toString(), Authorization.PRIVILEGES, a(DataStructureUtils.m(Authorization.PrivilegeType.ADMIN.toString(), Authorization.PrivilegeState.ON.toString())))))); Authorization authorization = templateConfig.getAuthorization(); assertThat(authorization.getAdminsConfig().size(), Matchers.is(1)); assertThat(authorization.getAdminsConfig(), hasItem(new AdminUser(new CaseInsensitiveString("geezer")))); } @Test public void validate_shouldEnsureThatTemplateFollowsTheNameType() { BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); PipelineTemplateConfig config = new PipelineTemplateConfig(new CaseInsensitiveString(".Abc")); config.validate(ConfigSaveValidationContext.forChain(cruiseConfig)); assertThat(config.errors().isEmpty(), is(false)); assertThat(config.errors().on(PipelineTemplateConfig.NAME), is("Invalid template name '.Abc'. This must be alphanumeric and can contain underscores and periods (however, it cannot start with a period). The maximum allowed length is 255 characters.")); } @Test public void shouldErrorOutWhenTryingToAddTwoStagesWithSameName() { BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); PipelineTemplateConfig pipelineTemplateConfig = new PipelineTemplateConfig(new CaseInsensitiveString("template"), StageConfigMother.manualStage("stage1"), StageConfigMother.manualStage("stage1")); pipelineTemplateConfig.validate(ConfigSaveValidationContext.forChain(cruiseConfig)); assertThat(pipelineTemplateConfig.get(0).errors().isEmpty(), is(false)); assertThat(pipelineTemplateConfig.get(1).errors().isEmpty(), is(false)); assertThat(pipelineTemplateConfig.get(0).errors().on(StageConfig.NAME), is("You have defined multiple stages called 'stage1'. Stage names are case-insensitive and must be unique.")); assertThat(pipelineTemplateConfig.get(1).errors().on(StageConfig.NAME), is("You have defined multiple stages called 'stage1'. Stage names are case-insensitive and must be unique.")); } @Test public void shouldValidateRoleNamesInTemplateAdminAuthorization() { BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); ServerConfig serverConfig = new ServerConfig(new SecurityConfig(null, null, false, new AdminsConfig(new AdminUser(new CaseInsensitiveString("admin")))), null); cruiseConfig.setServerConfig(serverConfig); GoConfigMother.enableSecurityWithPasswordFile(cruiseConfig); RoleConfig roleConfig = new RoleConfig(new CaseInsensitiveString("non-existent-role"), new RoleUser("non-existent-user")); PipelineTemplateConfig template = new PipelineTemplateConfig(new CaseInsensitiveString("template"), new Authorization(new AdminsConfig(new AdminRole(roleConfig))), StageConfigMother.manualStage("stage2"), StageConfigMother.manualStage("stage")); template.validate(ConfigSaveValidationContext.forChain(cruiseConfig)); assertThat(template.getAllErrors().get(0).getAllOn("name"), is(Arrays.asList("Role \"non-existent-role\" does not exist."))); } @Test public void shouldValidateRoleNamesInTemplateViewAuthorization() { BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); ServerConfig serverConfig = new ServerConfig(new SecurityConfig(null, null, false, new AdminsConfig(new AdminUser(new CaseInsensitiveString("admin")))), null); cruiseConfig.setServerConfig(serverConfig); GoConfigMother.enableSecurityWithPasswordFile(cruiseConfig); RoleConfig roleConfig = new RoleConfig(new CaseInsensitiveString("non-existent-role"), new RoleUser("non-existent-user")); PipelineTemplateConfig template = new PipelineTemplateConfig(new CaseInsensitiveString("template"), new Authorization(new ViewConfig(new AdminRole(roleConfig))), StageConfigMother.manualStage("stage2"), StageConfigMother.manualStage("stage")); template.validate(ConfigSaveValidationContext.forChain(cruiseConfig)); assertThat(template.getAllErrors().get(0).getAllOn("name"), is(Arrays.asList("Role \"non-existent-role\" does not exist."))); } @Test public void shouldUnderstandUsedParams() { PipelineTemplateConfig template = PipelineTemplateConfigMother.createTemplateWithParams("template-name", "foo", "bar", "baz"); ParamsConfig params = template.referredParams(); assertThat(params.size(), is(3)); assertThat(params, hasItem(new ParamConfig("foo", null))); assertThat(params, hasItem(new ParamConfig("bar", null))); assertThat(params, hasItem(new ParamConfig("baz", null))); params = template.referredParams();//should not mutate assertThat(params.size(), is(3)); } @Test public void shouldValidateWhetherTheReferredParamsAreDefinedInPipelinesUsingTheTemplate() { PipelineTemplateConfig templateWithParams = PipelineTemplateConfigMother.createTemplateWithParams("template", "param1", "param2"); PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfigWithTemplate("pipeline", "template"); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(templateWithParams); cruiseConfig.addPipelineWithoutValidation("group", pipelineConfig); templateWithParams.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(templateWithParams.errors().getAllOn("params"), is(Arrays.asList("The param 'param1' is not defined in pipeline 'pipeline'", "The param 'param2' is not defined in pipeline 'pipeline'"))); } @Test public void shouldValidateFetchTasksOfATemplateInTheContextOfPipelinesUsingTheTemplate() throws Exception { JobConfig jobConfig = new JobConfig(new CaseInsensitiveString("defaultJob")); jobConfig.addTask(new FetchTask(new CaseInsensitiveString("non-existent-pipeline"), new CaseInsensitiveString("stage"), new CaseInsensitiveString("job"), "src", "dest")); JobConfigs jobConfigs = new JobConfigs(jobConfig); StageConfig stageConfig = StageConfigMother.custom("stage", jobConfigs); PipelineTemplateConfig template = PipelineTemplateConfigMother.createTemplate("template", stageConfig); PipelineConfig pipelineConfig = PipelineConfigMother.pipelineConfigWithTemplate("pipeline", "template"); pipelineConfig.usingTemplate(template); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(template); cruiseConfig.addPipelineWithoutValidation("group", pipelineConfig); template.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(template.errors().getAllOn("pipeline"), is(Arrays.asList("\"pipeline :: stage :: defaultJob\" tries to fetch artifact from pipeline \"non-existent-pipeline\" which does not exist."))); } @Test public void shouldValidateTemplateStageUsedInDownstreamPipelines() { JobConfig jobConfigWithExecTask = new JobConfig(new CaseInsensitiveString("defaultJob")); jobConfigWithExecTask.addTask(new ExecTask("ls", "l", "server/config")); JobConfigs jobConfigs = new JobConfigs(jobConfigWithExecTask); StageConfig stageConfig = StageConfigMother.custom("stage", jobConfigs); PipelineTemplateConfig template = PipelineTemplateConfigMother.createTemplate("template", stageConfig); PipelineConfig upstreamPipelineUsingTemplate = PipelineConfigMother.pipelineConfigWithTemplate("pipeline", "template"); upstreamPipelineUsingTemplate.usingTemplate(template); //Pipeline and stage of upstreamPipelineUsingTemplate MaterialConfig dependency = new DependencyMaterialConfig(new CaseInsensitiveString("pipeline"), new CaseInsensitiveString("non-existent-stage")); PipelineConfig downStreamPipeline = PipelineConfigMother.pipelineConfig("downstreamPipeline", new MaterialConfigs(dependency)); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(template); cruiseConfig.addPipelineWithoutValidation("group", upstreamPipelineUsingTemplate); cruiseConfig.addPipelineWithoutValidation("group", downStreamPipeline); template.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(template.errors().getAllOn("base"), is(Arrays.asList("Stage with name 'non-existent-stage' does not exist on pipeline 'pipeline', it is being referred to from pipeline 'downstreamPipeline' (cruise-config.xml)"))); } @Test public void shouldValidateTemplateJobsUsedInDownstreamPipelines() { JobConfig jobConfigWithExecTask = new JobConfig(new CaseInsensitiveString("defaultJob")); jobConfigWithExecTask.addTask(new ExecTask("ls", "l", "server/config")); JobConfigs jobConfigs = new JobConfigs(jobConfigWithExecTask); JobConfig jobConfigWithFetchTask = new JobConfig(new CaseInsensitiveString("fetchJob")); jobConfigWithFetchTask.addTask(new FetchTask(new CaseInsensitiveString("pipeline"), new CaseInsensitiveString("stage"), new CaseInsensitiveString("non-existent-job"), "src", "dest")); JobConfigs jobConfigsForDownstream = new JobConfigs(jobConfigWithFetchTask); StageConfig stageConfig = StageConfigMother.custom("stage", jobConfigs); PipelineTemplateConfig template = PipelineTemplateConfigMother.createTemplate("template", stageConfig); PipelineConfig upstreamPipelineUsingTemplate = PipelineConfigMother.pipelineConfigWithTemplate("pipeline", "template"); upstreamPipelineUsingTemplate.usingTemplate(template); //Pipeline and stage of upstreamPipelineUsingTemplate MaterialConfig dependency = new DependencyMaterialConfig(new CaseInsensitiveString("pipeline"), new CaseInsensitiveString("stage")); PipelineConfig downStreamPipeline = PipelineConfigMother.pipelineConfig("downstreamPipeline", new MaterialConfigs(dependency), jobConfigsForDownstream); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(template); cruiseConfig.addPipelineWithoutValidation("group", upstreamPipelineUsingTemplate); cruiseConfig.addPipelineWithoutValidation("group", downStreamPipeline); template.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(template.errors().getAllOn("base"), is(Arrays.asList("\"downstreamPipeline :: mingle :: fetchJob\" tries to fetch artifact from job \"pipeline :: stage :: non-existent-job\" which does not exist."))); } @Test public void shouldAllowEditingOfStageNameWhenItIsNotUsedAsDependencyMaterial() throws Exception { PipelineTemplateConfig template = new PipelineTemplateConfig(new CaseInsensitiveString("template"), StageConfigMother.oneBuildPlanWithResourcesAndMaterials("stage2")); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(template); template.getStages().get(0).setName(new CaseInsensitiveString("updatedStageName")); template.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(template.errors().isEmpty(), is(true)); } @Test public void shouldAllowEditingOfJobNameWhenItIsNotUsedAsFetchArtifact() throws Exception { PipelineTemplateConfig template = new PipelineTemplateConfig(new CaseInsensitiveString("template"), StageConfigMother.oneBuildPlanWithResourcesAndMaterials("stage", "job2")); BasicCruiseConfig cruiseConfig = GoConfigMother.defaultCruiseConfig(); cruiseConfig.addTemplate(template); template.getStages().get(0).getJobs().get(0).setName(new CaseInsensitiveString("updatedJobName")); template.validateTree(ConfigSaveValidationContext.forChain(cruiseConfig), cruiseConfig, false); assertThat(template.errors().isEmpty(), is(true)); } @Test public void copyStagesShouldNotThrowExceptionIfInputPipelineConfigIsNull() { PipelineTemplateConfig template = PipelineTemplateConfigMother.createTemplateWithParams("template-name", "foo", "bar", "baz"); int sizeBeforeCopy = template.size(); template.copyStages(null); assertThat(template.size(), is(sizeBeforeCopy)); } }