/*************************GO-LICENSE-START********************************* * Copyright 2014 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.server.service; import com.thoughtworks.go.config.CaseInsensitiveString; import com.thoughtworks.go.config.CruiseConfig; import com.thoughtworks.go.config.PipelineConfig; import com.thoughtworks.go.domain.PipelinePauseInfo; import com.thoughtworks.go.i18n.LocalizedMessage; import com.thoughtworks.go.server.dao.PipelineSqlMapDao; import com.thoughtworks.go.server.domain.Username; import com.thoughtworks.go.server.service.result.DefaultLocalizedOperationResult; import com.thoughtworks.go.server.service.result.LocalizedOperationResult; import com.thoughtworks.go.server.util.UserHelper; import com.thoughtworks.go.serverhealth.HealthStateScope; import com.thoughtworks.go.serverhealth.HealthStateType; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PipelinePauseService { private PipelineSqlMapDao pipelineSqlMapDao; private final GoConfigService goConfigService; private final SecurityService securityService; private static final Logger LOGGER = Logger.getLogger(PipelinePauseService.class); @Autowired public PipelinePauseService(PipelineSqlMapDao pipelineSqlMapDao, GoConfigService goConfigService, SecurityService securityService) { this.pipelineSqlMapDao = pipelineSqlMapDao; this.goConfigService = goConfigService; this.securityService = securityService; } public void pause(String pipelineName, String pauseCause, Username userName) { pause(pipelineName, pauseCause, userName, new DefaultLocalizedOperationResult()); } public void pause(String pipelineName, String pauseCause, Username pausedBy, LocalizedOperationResult result) { String pauseByUserName = pausedBy == null ? "" : pausedBy.getUsername().toString(); if (pipelineDoesNotExist(pipelineName, result) || notAuthorized(pipelineName, pauseByUserName, result)) { return; } if(StringUtils.isBlank(pauseCause)) { pauseCause = ""; } try { pausePipeline(pipelineName, pauseCause, pausedBy); } catch (Exception e) { LOGGER.error("[Pipeline Pause] Failed to pause pipeline", e); result.internalServerError(LocalizedMessage.string("INTERNAL_SERVER_ERROR")); } } private void pausePipeline(String pipelineName, String pauseCause, Username pauseBy) { String mutexPipelineName = mutexForPausePipeline(pipelineName); synchronized (mutexPipelineName) { String sanitizedPauseCause = pauseCause.substring(0, Math.min(255, pauseCause.length())); String pauseByDisplayName = pauseBy.getDisplayName(); String sanitizedPauseBy = pauseByDisplayName.substring(0, Math.min(255, pauseByDisplayName.length())); pipelineSqlMapDao.pause(pipelineName, sanitizedPauseCause, sanitizedPauseBy); LOGGER.info(String.format("[Pipeline Pause] Pipeline [%s] is paused by [%s] because [%s]", pipelineName, pauseBy, pauseCause)); } } private boolean notAuthorized(String pipelineName, String pauseBy, LocalizedOperationResult result) { CruiseConfig cruiseConfig = goConfigService.getCurrentConfig(); PipelineConfig pipelineConfig = cruiseConfig.pipelineConfigByName(new CaseInsensitiveString(pipelineName)); if (securityService.hasOperatePermissionForGroup(new CaseInsensitiveString(pauseBy), cruiseConfig.findGroupOfPipeline(pipelineConfig).getGroup())) { return false; } result.unauthorized(LocalizedMessage.string("UNAUTHORIZED_TO_EDIT_PIPELINE", pipelineName), HealthStateType.unauthorisedForPipeline(pipelineName)); return true; } private boolean pipelineDoesNotExist(String pipelineName, LocalizedOperationResult result) { CruiseConfig cruiseConfig = goConfigService.getCurrentConfig(); if (cruiseConfig.hasPipelineNamed(new CaseInsensitiveString(pipelineName))) { return false; } result.notFound(LocalizedMessage.string("RESOURCE_NOT_FOUND", "pipeline", pipelineName), HealthStateType.general(HealthStateScope.forPipeline(pipelineName))); return true; } public void unpause(String pipelineName) { unpause(pipelineName, UserHelper.getUserName(), new DefaultLocalizedOperationResult()); } public void unpause(String pipelineName, Username unpausedBy, LocalizedOperationResult result) { String unpauseByUserName = unpausedBy == null ? "" : unpausedBy.getUsername().toString(); if (pipelineDoesNotExist(pipelineName, result) || notAuthorized(pipelineName, unpauseByUserName, result)) { return; } try { unpausePipeline(pipelineName, unpausedBy); } catch (Exception e) { LOGGER.error("[Pipeline Unpause] Failed to unpause pipeline", e); result.internalServerError(LocalizedMessage.string("INTERNAL_SERVER_ERROR")); } } private void unpausePipeline(String pipelineName, Username unpausedBy) { String mutextPipelineName = mutexForPausePipeline(pipelineName); synchronized (mutextPipelineName) { pipelineSqlMapDao.unpause(pipelineName); LOGGER.info(String.format("[Pipeline Unpause] Pipeline [%s] is unpaused by [%s]", pipelineName,unpausedBy)); } } public PipelinePauseInfo pipelinePauseInfo(String pipelineName) { PipelinePauseInfo pipelinePauseInfo = pipelineSqlMapDao.pauseState(pipelineName); return pipelinePauseInfo == null ? PipelinePauseInfo.notPaused() : pipelinePauseInfo; } public boolean isPaused(String pipelineName) { return pipelinePauseInfo(pipelineName).isPaused(); } /* * Mutex shared between PipelinePauseService and PipelineService. This is to avoid constraint violation when * updateCounter() and pause() are trying to insert pipeline row if one doesn't exist */ public static String mutexForPausePipeline(String pipelineName) { return (PipelineSqlMapDao.class.getName() + "_mutexForPausePipeline_" + pipelineName).intern(); } }