/*
* 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 static java.lang.String.format;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.domain.StageIdentifier;
import com.thoughtworks.go.server.dao.PipelineStateDao;
import com.thoughtworks.go.server.domain.Username;
import com.thoughtworks.go.server.service.result.OperationResult;
import com.thoughtworks.go.serverhealth.HealthStateScope;
import com.thoughtworks.go.serverhealth.HealthStateType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @understands forced unlocking of pipeline by a user
*
* unlock belongs in PipelineLockService but had to be pulled into a separate service
* because of circular dependency between CachedCurrentActivityService and PipelineLockService
* (unlock still is in PipelineLockService, the unlock here does necessary validity checks
* and delegates to PipelineLockService, and PipelineLockService is the only one dealing with
* locking and unlocking of pipelines)
*/
@Service
public class PipelineUnlockApiService {
private final GoConfigService goConfigService;
private final PipelineLockService pipelineLockService;
private final CurrentActivityService currentActivityService;
private final SecurityService securityService;
@Autowired
public PipelineUnlockApiService(GoConfigService goConfigService, PipelineLockService pipelineLockService,
CurrentActivityService currentActivityService, SecurityService securityService) {
this.goConfigService = goConfigService;
this.pipelineLockService = pipelineLockService;
this.currentActivityService = currentActivityService;
this.securityService = securityService;
}
public void unlock(String pipelineName, Username username, OperationResult result) {
if (canUnlock(pipelineName, username, result)) {
pipelineLockService.unlock(pipelineName);
result.ok("pipeline lock released for " + pipelineName);
}
}
public boolean canUnlock(String pipelineName, Username username, OperationResult result) {
if (!goConfigService.hasPipelineNamed(new CaseInsensitiveString(pipelineName))) {
String msg = format("pipeline name %s is incorrect", pipelineName);
result.notFound(msg, msg, HealthStateType.general(HealthStateScope.forPipeline(pipelineName)));
return false;
}
if (!securityService.hasOperatePermissionForPipeline(username.getUsername(), pipelineName)) {
String msg = "user does not have operate permission on the pipeline";
result.unauthorized(msg, msg, HealthStateType.general(HealthStateScope.forPipeline(pipelineName)));
return false;
}
if (!goConfigService.isLockable(pipelineName)) {
result.notAcceptable(format("no lock exists within the pipeline configuration for %s", pipelineName), HealthStateType.general(HealthStateScope.GLOBAL));
return false;
}
StageIdentifier stageIdentifier = pipelineLockService.lockedPipeline(pipelineName);
if (stageIdentifier == null) {
result.notAcceptable("lock exists within the pipeline configuration but no pipeline instance is currently in progress", HealthStateType.general(HealthStateScope.GLOBAL));
return false;
}
if (currentActivityService.isAnyStageActive(stageIdentifier.pipelineIdentifier())) {
result.notAcceptable("locked pipeline instance is currently running (one of the stages is in progress)", HealthStateType.general(HealthStateScope.GLOBAL));
return false;
}
return true;
}
}