/* * Copyright 2016 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.*; import com.thoughtworks.go.config.commands.EntityConfigUpdateCommand; import com.thoughtworks.go.config.exceptions.GoConfigInvalidException; import com.thoughtworks.go.config.pluggabletask.PluggableTask; import com.thoughtworks.go.config.remote.ConfigOrigin; import com.thoughtworks.go.config.update.ConfigUpdateCheckFailedException; import com.thoughtworks.go.config.update.CreatePipelineConfigCommand; import com.thoughtworks.go.config.update.DeletePipelineConfigCommand; import com.thoughtworks.go.config.update.UpdatePipelineConfigCommand; import com.thoughtworks.go.domain.Task; import com.thoughtworks.go.i18n.LocalizedMessage; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.presentation.CanDeleteResult; import com.thoughtworks.go.server.service.result.LocalizedOperationResult; import com.thoughtworks.go.server.service.tasks.PluggableTaskService; import com.thoughtworks.go.util.Node; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; /** * @understands providing services around a pipeline configuration */ @Service public class PipelineConfigService { private final GoConfigService goConfigService; private final SecurityService securityService; private final PluggableTaskService pluggableTaskService; private final EntityHashingService entityHashingService; private static final org.apache.log4j.Logger LOGGER = org.apache.log4j.Logger.getLogger(PipelineConfigService.class); @Autowired public PipelineConfigService(GoConfigService goConfigService, SecurityService securityService, PluggableTaskService pluggableTaskService, EntityHashingService entityHashingService) { this.goConfigService = goConfigService; this.securityService = securityService; this.pluggableTaskService = pluggableTaskService; this.entityHashingService = entityHashingService; } public Map<CaseInsensitiveString, CanDeleteResult> canDeletePipelines() { CruiseConfig cruiseConfig = goConfigService.getCurrentConfig(); Map<CaseInsensitiveString, CanDeleteResult> nameToCanDeleteIt = new HashMap<>(); Hashtable<CaseInsensitiveString, Node> hashtable = cruiseConfig.getDependencyTable(); List<CaseInsensitiveString> pipelineNames = cruiseConfig.getAllPipelineNames(); for (CaseInsensitiveString pipelineName : pipelineNames) { ConfigOrigin origin = pipelineConfigOrigin(cruiseConfig,pipelineName); if(origin != null && !origin.isLocal()) { nameToCanDeleteIt.put(pipelineName, new CanDeleteResult(false, LocalizedMessage.string("CANNOT_DELETE_REMOTE_PIPELINE", pipelineName, origin.displayName()))); } else { CaseInsensitiveString envName = environmentUsedIn(cruiseConfig, pipelineName); if (envName != null) { nameToCanDeleteIt.put(pipelineName, new CanDeleteResult(false, LocalizedMessage.string("CANNOT_DELETE_PIPELINE_IN_ENVIRONMENT", pipelineName, envName))); } else { CaseInsensitiveString downStream = downstreamOf(hashtable, pipelineName); if (downStream != null) { nameToCanDeleteIt.put(pipelineName, new CanDeleteResult(false, LocalizedMessage.string("CANNOT_DELETE_PIPELINE_USED_AS_MATERIALS", pipelineName, downStream))); } else { nameToCanDeleteIt.put(pipelineName, new CanDeleteResult(true, LocalizedMessage.string("CAN_DELETE_PIPELINE"))); } } } } return nameToCanDeleteIt; } private ConfigOrigin pipelineConfigOrigin(CruiseConfig cruiseConfig,final CaseInsensitiveString pipelineName) { PipelineConfig pipelineConfig = cruiseConfig.pipelineConfigByName(pipelineName); if(pipelineConfig == null) return null; return pipelineConfig.getOrigin(); } private CaseInsensitiveString downstreamOf(Hashtable<CaseInsensitiveString, Node> pipelineToUpstream, final CaseInsensitiveString pipelineName) { for (Map.Entry<CaseInsensitiveString, Node> entry : pipelineToUpstream.entrySet()) { if (entry.getValue().hasDependency(pipelineName)) { return entry.getKey(); } } return null; } private CaseInsensitiveString environmentUsedIn(CruiseConfig cruiseConfig, final CaseInsensitiveString pipelineName) { return cruiseConfig.getEnvironments().findEnvironmentNameForPipeline(pipelineName); } public PipelineConfig getPipelineConfig(String pipelineName) { return goConfigService.getMergedConfigForEditing().getPipelineConfigByName(new CaseInsensitiveString(pipelineName)); } private void update(Username currentUser, PipelineConfig pipelineConfig, LocalizedOperationResult result, EntityConfigUpdateCommand command) { try { goConfigService.updateConfig(command, currentUser); } catch (Exception e) { if (e instanceof GoConfigInvalidException) { if(!result.hasMessage()){ result.unprocessableEntity(LocalizedMessage.string("ENTITY_CONFIG_VALIDATION_FAILED", pipelineConfig.getClass().getAnnotation(ConfigTag.class).value(), CaseInsensitiveString.str(pipelineConfig.name()), e.getMessage())); } } else if (!(e instanceof ConfigUpdateCheckFailedException)) { LOGGER.error(e.getMessage(), e); result.internalServerError(LocalizedMessage.string("SAVE_FAILED_WITH_REASON", e.getMessage())); } } } public void updatePipelineConfig(final Username currentUser, final PipelineConfig pipelineConfig, final String md5, final LocalizedOperationResult result) { validatePluggableTasks(pipelineConfig); UpdatePipelineConfigCommand updatePipelineConfigCommand = new UpdatePipelineConfigCommand(goConfigService, entityHashingService, pipelineConfig, currentUser, md5, result); update(currentUser, pipelineConfig, result, updatePipelineConfigCommand); } public List<PipelineConfigs> viewableGroupsFor(Username username) { ArrayList<PipelineConfigs> list = new ArrayList<>(); for (PipelineConfigs pipelineConfigs : goConfigService.cruiseConfig().getGroups()) { if (securityService.hasViewPermissionForGroup(CaseInsensitiveString.str(username.getUsername()), pipelineConfigs.getGroup())) { list.add(pipelineConfigs); } } return list; } public List<PipelineConfigs> viewableOrOperatableGroupsFor(Username username) { ArrayList<PipelineConfigs> list = new ArrayList<>(); for (PipelineConfigs pipelineConfigs : goConfigService.cruiseConfig().getGroups()) { if(hasViewOrOperatePermissionForGroup(username, pipelineConfigs.getGroup())) { list.add(pipelineConfigs); } } return list; } public void createPipelineConfig(final Username currentUser, final PipelineConfig pipelineConfig, final LocalizedOperationResult result, final String groupName) { validatePluggableTasks(pipelineConfig); CreatePipelineConfigCommand createPipelineConfigCommand = new CreatePipelineConfigCommand(goConfigService, pipelineConfig, currentUser, result, groupName); update(currentUser, pipelineConfig, result, createPipelineConfigCommand); } public void deletePipelineConfig(final Username currentUser, final PipelineConfig pipelineConfig, final LocalizedOperationResult result) { DeletePipelineConfigCommand deletePipelineConfigCommand = new DeletePipelineConfigCommand(goConfigService, pipelineConfig, currentUser, result); update(currentUser, pipelineConfig, result, deletePipelineConfigCommand); if(result.isSuccessful()) { result.setMessage(LocalizedMessage.string("RESOURCE_DELETE_SUCCESSFUL", "pipeline", pipelineConfig.name())); } } private boolean hasViewOrOperatePermissionForGroup(Username username, String group) { return securityService.hasViewPermissionForGroup(CaseInsensitiveString.str(username.getUsername()), group) || securityService.hasOperatePermissionForGroup(username.getUsername(), group); } private void validatePluggableTasks(PipelineConfig config) { for(PluggableTask task: pluggableTask(config)) { pluggableTaskService.isValid(task); } } private List<PluggableTask> pluggableTask(PipelineConfig config) { List<PluggableTask> tasks = new ArrayList<>(); for(StageConfig stageConfig: config.getStages()) { for(JobConfig jobConfig: stageConfig.getJobs()) { for(Task task: jobConfig.getTasks()) { if(task instanceof PluggableTask) { tasks.add((PluggableTask) task); } } } } return tasks; } }