/*
* 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.commands.EntityConfigUpdateCommand;
import com.thoughtworks.go.config.exceptions.GoConfigInvalidException;
import com.thoughtworks.go.config.pluggabletask.PluggableTask;
import com.thoughtworks.go.config.update.CreateTemplateConfigCommand;
import com.thoughtworks.go.config.update.DeleteTemplateConfigCommand;
import com.thoughtworks.go.config.update.UpdateTemplateConfigCommand;
import com.thoughtworks.go.domain.Task;
import com.thoughtworks.go.i18n.LocalizedMessage;
import com.thoughtworks.go.presentation.ConfigForEdit;
import com.thoughtworks.go.server.domain.Username;
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
import com.thoughtworks.go.server.service.tasks.PluggableTaskService;
import com.thoughtworks.go.server.ui.TemplatesViewModel;
import com.thoughtworks.go.serverhealth.HealthStateScope;
import com.thoughtworks.go.serverhealth.HealthStateType;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class TemplateConfigService {
private final GoConfigService goConfigService;
private final SecurityService securityService;
private org.slf4j.Logger LOGGER = LoggerFactory.getLogger(TemplateConfigService.class);
private Cloner cloner = new Cloner();
private EntityHashingService entityHashingService;
private PluggableTaskService pluggableTaskService;
@Autowired
public TemplateConfigService(GoConfigService goConfigService, SecurityService securityService, EntityHashingService entityHashingService, PluggableTaskService pluggableTaskService) {
this.goConfigService = goConfigService;
this.securityService = securityService;
this.entityHashingService = entityHashingService;
this.pluggableTaskService = pluggableTaskService;
}
public Map<CaseInsensitiveString, List<CaseInsensitiveString>> templatesWithPipelinesForUser(String username) {
return goConfigService.getCurrentConfig().templatesWithPipelinesForUser(username, goConfigService.rolesForUser(new CaseInsensitiveString(username)));
}
public void removeTemplate(String templateName, CruiseConfig cruiseConfig, String md5, HttpLocalizedOperationResult result) {
if (!doesTemplateExist(templateName, cruiseConfig, result)) {
return;
}
goConfigService.updateConfig(new DeleteTemplateCommand(templateName, md5));
}
public void createTemplateConfig(final Username currentUser, final PipelineTemplateConfig templateConfig, final LocalizedOperationResult result) {
validatePluggableTasks(templateConfig);
CreateTemplateConfigCommand command = new CreateTemplateConfigCommand(templateConfig, currentUser, goConfigService, result);
update(currentUser, result, command, templateConfig);
}
public void updateTemplateConfig(final Username currentUser, final PipelineTemplateConfig templateConfig, final LocalizedOperationResult result, String md5) {
validatePluggableTasks(templateConfig);
UpdateTemplateConfigCommand command = new UpdateTemplateConfigCommand(templateConfig, currentUser, securityService, result, md5, entityHashingService);
update(currentUser, result, command, templateConfig);
}
public void deleteTemplateConfig(final Username currentUser, final PipelineTemplateConfig templateConfig, final LocalizedOperationResult result) {
DeleteTemplateConfigCommand command = new DeleteTemplateConfigCommand(templateConfig, result, securityService, currentUser);
update(currentUser, result, command, templateConfig);
if (result.isSuccessful()) {
result.setMessage(LocalizedMessage.string("RESOURCE_DELETE_SUCCESSFUL", "template", templateConfig.name().toString()));
}
}
private void update(Username currentUser, LocalizedOperationResult result, EntityConfigUpdateCommand command, PipelineTemplateConfig templateConfig) {
try {
goConfigService.updateConfig(command, currentUser);
} catch (Exception e) {
if (e instanceof GoConfigInvalidException) {
result.unprocessableEntity(LocalizedMessage.string("ENTITY_CONFIG_VALIDATION_FAILED", "template", templateConfig.name(), e.getMessage()));
} else {
if (!result.hasMessage()) {
LOGGER.error(e.getMessage(), e);
result.internalServerError(LocalizedMessage.string("SAVE_FAILED_WITH_REASON", "An error occurred while saving the template config. Please check the logs for more information."));
}
}
}
}
private void validatePluggableTasks(PipelineTemplateConfig templateConfig) {
for (PluggableTask task : getPluggableTask(templateConfig)) {
pluggableTaskService.isValid(task);
}
}
private List<PluggableTask> getPluggableTask(PipelineTemplateConfig templateConfig) {
List<PluggableTask> pluggableTasks = new ArrayList<>();
for (StageConfig stage : templateConfig.getStages()) {
for (JobConfig job : stage.getJobs()) {
for (Task task : job.getTasks()) {
if (task instanceof PluggableTask) {
pluggableTasks.add((PluggableTask) task);
}
}
}
}
return pluggableTasks;
}
public ConfigForEdit<PipelineTemplateConfig> loadForEdit(String templateName, Username username, HttpLocalizedOperationResult result) {
if (!securityService.isAuthorizedToEditTemplate(templateName, username)) {
result.unauthorized(LocalizedMessage.string("UNAUTHORIZED_TO_EDIT_TEMPLATE", templateName), HealthStateType.unauthorised());
return null;
}
GoConfigHolder configHolder = goConfigService.getConfigHolder();
configHolder = cloner.deepClone(configHolder);
PipelineTemplateConfig template = findTemplate(templateName, result, configHolder);
return template != null ? new ConfigForEdit<>(template, configHolder) : null;
}
public PipelineTemplateConfig loadForView(String templateName, HttpLocalizedOperationResult result) {
return findTemplate(templateName, result, goConfigService.getConfigHolder());
}
private boolean doesTemplateExist(String templateName, CruiseConfig cruiseConfig, HttpLocalizedOperationResult result) {
TemplatesConfig templates = cruiseConfig.getTemplates();
if (!templates.hasTemplateNamed(new CaseInsensitiveString(templateName))) {
result.notFound(LocalizedMessage.string("RESOURCE_NOT_FOUND", "Template", templateName), HealthStateType.general(HealthStateScope.GLOBAL));
return false;
}
return true;
}
public List<PipelineConfig> allPipelinesNotUsingTemplates(Username username, LocalizedOperationResult result) {
if (!securityService.isUserAdmin(username)) {
result.unauthorized(LocalizedMessage.string("UNAUTHORIZED_TO_ADMINISTER"), HealthStateType.unauthorised());
return null;
}
List<PipelineConfig> allPipelineConfigs = goConfigService.getAllPipelineConfigsForEdit();
List<PipelineConfig> allPipelinesNotUsingTemplates = new ArrayList<>();
for (PipelineConfig pipeline : allPipelineConfigs) {
if (!pipeline.hasTemplate()) {
allPipelinesNotUsingTemplates.add(pipeline);
}
}
return allPipelinesNotUsingTemplates;
}
public List<TemplatesViewModel> getTemplateViewModels(CaseInsensitiveString username) {
List<TemplatesViewModel> templatesViewModels = new ArrayList<>();
CruiseConfig cruiseConfig = goConfigService.cruiseConfig();
for (PipelineTemplateConfig templateConfig : cruiseConfig.getTemplates()) {
boolean authorizedToViewTemplate = cruiseConfig.isAuthorizedToViewTemplate(templateConfig.name().toString(), username);
boolean authorizedToEditTemplate = cruiseConfig.isAuthorizedToEditTemplate(templateConfig.name().toString(), username);
templatesViewModels.add(new TemplatesViewModel(templateConfig, authorizedToViewTemplate, authorizedToEditTemplate));
}
return templatesViewModels;
}
private PipelineTemplateConfig findTemplate(String templateName, HttpLocalizedOperationResult result, GoConfigHolder configHolder) {
if (!doesTemplateExist(templateName, configHolder.configForEdit, result)) {
return null;
}
return configHolder.configForEdit.findTemplate(new CaseInsensitiveString(templateName));
}
}