package com.ctrip.framework.apollo.biz.service;
import com.google.common.collect.Maps;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.GrayReleaseRuleRepository;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;
import com.ctrip.framework.apollo.common.utils.UniqueKeyGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@Service
public class NamespaceBranchService {
@Autowired
private AuditService auditService;
@Autowired
private GrayReleaseRuleRepository grayReleaseRuleRepository;
@Autowired
private ClusterService clusterService;
@Autowired
private ReleaseService releaseService;
@Autowired
private NamespaceService namespaceService;
@Autowired
private ReleaseHistoryService releaseHistoryService;
@Transactional
public Namespace createBranch(String appId, String parentClusterName, String namespaceName, String operator){
Namespace childNamespace = findBranch(appId, parentClusterName, namespaceName);
if (childNamespace != null){
throw new BadRequestException("namespace already has branch");
}
Cluster parentCluster = clusterService.findOne(appId, parentClusterName);
if (parentCluster == null || parentCluster.getParentClusterId() != 0) {
throw new BadRequestException("cluster not exist or illegal cluster");
}
//create child cluster
Cluster childCluster = createChildCluster(appId, parentCluster, namespaceName, operator);
Cluster createdChildCluster = clusterService.saveWithoutInstanceOfAppNamespaces(childCluster);
//create child namespace
childNamespace = createNamespaceBranch(appId, createdChildCluster.getName(),
namespaceName, operator);
return namespaceService.save(childNamespace);
}
public Namespace findBranch(String appId, String parentClusterName, String namespaceName) {
return namespaceService.findChildNamespace(appId, parentClusterName, namespaceName);
}
public GrayReleaseRule findBranchGrayRules(String appId, String clusterName, String namespaceName,
String branchName) {
return grayReleaseRuleRepository
.findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
}
@Transactional
public void updateBranchGrayRules(String appId, String clusterName, String namespaceName,
String branchName, GrayReleaseRule newRules) {
doUpdateBranchGrayRules(appId, clusterName, namespaceName, branchName, newRules, true, ReleaseOperation.APPLY_GRAY_RULES);
}
private void doUpdateBranchGrayRules(String appId, String clusterName, String namespaceName,
String branchName, GrayReleaseRule newRules, boolean recordReleaseHistory, int releaseOperation) {
GrayReleaseRule oldRules = grayReleaseRuleRepository
.findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
Release latestBranchRelease = releaseService.findLatestActiveRelease(appId, branchName, namespaceName);
long latestBranchReleaseId = latestBranchRelease != null ? latestBranchRelease.getId() : 0;
newRules.setReleaseId(latestBranchReleaseId);
grayReleaseRuleRepository.save(newRules);
//delete old rules
if (oldRules != null) {
grayReleaseRuleRepository.delete(oldRules);
}
if (recordReleaseHistory) {
Map<String, Object> releaseOperationContext = Maps.newHashMap();
releaseOperationContext.put(ReleaseOperationContext.RULES, GrayReleaseRuleItemTransformer
.batchTransformFromJSON(newRules.getRules()));
if (oldRules != null) {
releaseOperationContext.put(ReleaseOperationContext.OLD_RULES,
GrayReleaseRuleItemTransformer.batchTransformFromJSON(oldRules.getRules()));
}
releaseHistoryService.createReleaseHistory(appId, clusterName, namespaceName, branchName, latestBranchReleaseId,
latestBranchReleaseId, releaseOperation, releaseOperationContext, newRules.getDataChangeLastModifiedBy());
}
}
@Transactional
public GrayReleaseRule updateRulesReleaseId(String appId, String clusterName,
String namespaceName, String branchName,
long latestReleaseId, String operator) {
GrayReleaseRule oldRules = grayReleaseRuleRepository.
findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(appId, clusterName, namespaceName, branchName);
if (oldRules == null) {
return null;
}
GrayReleaseRule newRules = new GrayReleaseRule();
newRules.setBranchStatus(NamespaceBranchStatus.ACTIVE);
newRules.setReleaseId(latestReleaseId);
newRules.setRules(oldRules.getRules());
newRules.setAppId(oldRules.getAppId());
newRules.setClusterName(oldRules.getClusterName());
newRules.setNamespaceName(oldRules.getNamespaceName());
newRules.setBranchName(oldRules.getBranchName());
newRules.setDataChangeCreatedBy(operator);
newRules.setDataChangeLastModifiedBy(operator);
grayReleaseRuleRepository.save(newRules);
grayReleaseRuleRepository.delete(oldRules);
return newRules;
}
@Transactional
public void deleteBranch(String appId, String clusterName, String namespaceName,
String branchName, int branchStatus, String operator) {
Cluster toDeleteCluster = clusterService.findOne(appId, branchName);
if (toDeleteCluster == null) {
return;
}
Release latestBranchRelease = releaseService.findLatestActiveRelease(appId, branchName, namespaceName);
long latestBranchReleaseId = latestBranchRelease != null ? latestBranchRelease.getId() : 0;
//update branch rules
GrayReleaseRule deleteRule = new GrayReleaseRule();
deleteRule.setRules("[]");
deleteRule.setAppId(appId);
deleteRule.setClusterName(clusterName);
deleteRule.setNamespaceName(namespaceName);
deleteRule.setBranchName(branchName);
deleteRule.setBranchStatus(branchStatus);
deleteRule.setDataChangeLastModifiedBy(operator);
deleteRule.setDataChangeCreatedBy(operator);
doUpdateBranchGrayRules(appId, clusterName, namespaceName, branchName, deleteRule, false, -1);
//delete branch cluster
clusterService.delete(toDeleteCluster.getId(), operator);
int releaseOperation = branchStatus == NamespaceBranchStatus.MERGED ? ReleaseOperation
.GRAY_RELEASE_DELETED_AFTER_MERGE : ReleaseOperation.ABANDON_GRAY_RELEASE;
releaseHistoryService.createReleaseHistory(appId, clusterName, namespaceName, branchName, latestBranchReleaseId,
latestBranchReleaseId, releaseOperation, null, operator);
auditService.audit("Branch", toDeleteCluster.getId(), Audit.OP.DELETE, operator);
}
private Cluster createChildCluster(String appId, Cluster parentCluster,
String namespaceName, String operator) {
Cluster childCluster = new Cluster();
childCluster.setAppId(appId);
childCluster.setParentClusterId(parentCluster.getId());
childCluster.setName(UniqueKeyGenerator.generate(appId, parentCluster.getName(), namespaceName));
childCluster.setDataChangeCreatedBy(operator);
childCluster.setDataChangeLastModifiedBy(operator);
return childCluster;
}
private Namespace createNamespaceBranch(String appId, String clusterName, String namespaceName, String operator) {
Namespace childNamespace = new Namespace();
childNamespace.setAppId(appId);
childNamespace.setClusterName(clusterName);
childNamespace.setNamespaceName(namespaceName);
childNamespace.setDataChangeLastModifiedBy(operator);
childNamespace.setDataChangeCreatedBy(operator);
return childNamespace;
}
}