提交 4f3dd644 编写于 作者: J Jason Song 提交者: GitHub

Merge pull request #520 from lepdou/emergency_publish

emergency publish
...@@ -33,6 +33,7 @@ import java.util.stream.Collectors; ...@@ -33,6 +33,7 @@ import java.util.stream.Collectors;
@RestController @RestController
public class ReleaseController { public class ReleaseController {
private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings() private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults(); .trimResults();
...@@ -57,7 +58,7 @@ public class ReleaseController { ...@@ -57,7 +58,7 @@ public class ReleaseController {
} }
@RequestMapping("/releases") @RequestMapping("/releases")
public List<ReleaseDTO> findReleaseByIds(@RequestParam("releaseIds") String releaseIds){ public List<ReleaseDTO> findReleaseByIds(@RequestParam("releaseIds") String releaseIds) {
Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong) Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
...@@ -99,13 +100,14 @@ public class ReleaseController { ...@@ -99,13 +100,14 @@ public class ReleaseController {
@PathVariable("namespaceName") String namespaceName, @PathVariable("namespaceName") String namespaceName,
@RequestParam("name") String releaseName, @RequestParam("name") String releaseName,
@RequestParam(name = "comment", required = false) String releaseComment, @RequestParam(name = "comment", required = false) String releaseComment,
@RequestParam("operator") String operator) { @RequestParam("operator") String operator,
@RequestParam(name = "isEmergencyPublish", defaultValue = "false") boolean isEmergencyPublish) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName); Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId, throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName)); clusterName, namespaceName));
} }
Release release = releaseService.publish(namespace, releaseName, releaseComment, operator); Release release = releaseService.publish(namespace, releaseName, releaseComment, operator, isEmergencyPublish);
//send release message //send release message
Namespace parentNamespace = namespaceService.findParentNamespace(namespace); Namespace parentNamespace = namespaceService.findParentNamespace(namespace);
...@@ -122,31 +124,33 @@ public class ReleaseController { ...@@ -122,31 +124,33 @@ public class ReleaseController {
/** /**
*merge branch items to master and publish master * merge branch items to master and publish master
*
* @return published result * @return published result
*/ */
@Transactional @Transactional
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish", method = RequestMethod.POST) @RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish", method = RequestMethod.POST)
public ReleaseDTO updateAndPublish(@PathVariable("appId") String appId, public ReleaseDTO updateAndPublish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName, @PathVariable("namespaceName") String namespaceName,
@RequestParam("releaseName") String releaseName, @RequestParam("releaseName") String releaseName,
@RequestParam("branchName") String branchName, @RequestParam("branchName") String branchName,
@RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,
@RequestParam(name = "releaseComment", required = false) String releaseComment, @RequestParam(name = "releaseComment", required = false) String releaseComment,
@RequestBody ItemChangeSets changeSets) { @RequestParam(name = "isEmergencyPublish", defaultValue = "false") boolean isEmergencyPublish,
@RequestBody ItemChangeSets changeSets) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName); Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId, throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName)); clusterName, namespaceName));
} }
Release release = releaseService.mergeBranchChangeSetsAndRelease(namespace, branchName, Release release = releaseService.mergeBranchChangeSetsAndRelease(namespace, branchName, releaseName,
releaseName, releaseComment, changeSets); releaseComment, isEmergencyPublish, changeSets);
if (deleteBranch) { if (deleteBranch) {
namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName, namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName,
NamespaceBranchStatus.MERGED, changeSets.getDataChangeLastModifiedBy()); NamespaceBranchStatus.MERGED, changeSets.getDataChangeLastModifiedBy());
} }
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName), messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
......
...@@ -121,7 +121,7 @@ public class ReleaseControllerTest extends AbstractControllerTest { ...@@ -121,7 +121,7 @@ public class ReleaseControllerTest extends AbstractControllerTest {
.thenReturn(someNamespace); .thenReturn(someNamespace);
releaseController releaseController
.publish(someAppId, someCluster, someNamespaceName, someName, someComment, "test"); .publish(someAppId, someCluster, someNamespaceName, someName, someComment, "test", false);
verify(someMessageSender, times(1)) verify(someMessageSender, times(1))
.sendMessage(Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR) .sendMessage(Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
......
...@@ -13,6 +13,7 @@ import com.ctrip.framework.apollo.biz.entity.NamespaceLock; ...@@ -13,6 +13,7 @@ import com.ctrip.framework.apollo.biz.entity.NamespaceLock;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGenerator; import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGenerator;
import com.ctrip.framework.apollo.common.constants.GsonType;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation; import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext; import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets; import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
...@@ -43,12 +44,10 @@ import java.util.Set; ...@@ -43,12 +44,10 @@ import java.util.Set;
*/ */
@Service @Service
public class ReleaseService { public class ReleaseService {
private static final FastDateFormat TIMESTAMP_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss"); private static final FastDateFormat TIMESTAMP_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss");
private Gson gson = new Gson(); private Gson gson = new Gson();
private Type configurationTypeReference = new TypeToken<Map<String, String>>() {
}.getType();
@Autowired @Autowired
private ReleaseRepository releaseRepository; private ReleaseRepository releaseRepository;
@Autowired @Autowired
...@@ -123,12 +122,11 @@ public class ReleaseService { ...@@ -123,12 +122,11 @@ public class ReleaseService {
} }
@Transactional @Transactional
public Release mergeBranchChangeSetsAndRelease(Namespace namespace, String branchName, String public Release mergeBranchChangeSetsAndRelease(Namespace namespace, String branchName, String releaseName,
releaseName, String releaseComment, ItemChangeSets changeSets) { String releaseComment, boolean isEmergencyPublish,
NamespaceLock lock = namespaceLockService.findLock(namespace.getId()); ItemChangeSets changeSets) {
if (lock != null && lock.getDataChangeCreatedBy().equals(changeSets.getDataChangeLastModifiedBy())) {
throw new BadRequestException("config can not be published by yourself."); checkLock(namespace, isEmergencyPublish, changeSets.getDataChangeLastModifiedBy());
}
itemSetService.updateSet(namespace, changeSets); itemSetService.updateSet(namespace, changeSets);
...@@ -141,22 +139,19 @@ public class ReleaseService { ...@@ -141,22 +139,19 @@ public class ReleaseService {
Map<String, Object> operationContext = Maps.newHashMap(); Map<String, Object> operationContext = Maps.newHashMap();
operationContext.put(ReleaseOperationContext.SOURCE_BRANCH, branchName); operationContext.put(ReleaseOperationContext.SOURCE_BRANCH, branchName);
operationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, branchReleaseId); operationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, branchReleaseId);
operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);
Release release = masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems, return masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,
changeSets.getDataChangeLastModifiedBy(), changeSets.getDataChangeLastModifiedBy(),
ReleaseOperation.GRAY_RELEASE_MERGE_TO_MASTER, operationContext); ReleaseOperation.GRAY_RELEASE_MERGE_TO_MASTER, operationContext);
return release;
} }
@Transactional @Transactional
public Release publish(Namespace namespace, String releaseName, String releaseComment, public Release publish(Namespace namespace, String releaseName, String releaseComment,
String operator) { String operator, boolean isEmergencyPublish) {
NamespaceLock lock = namespaceLockService.findLock(namespace.getId()); checkLock(namespace, isEmergencyPublish, operator);
if (lock != null && lock.getDataChangeCreatedBy().equals(operator)) {
throw new BadRequestException("config can not be published by yourself.");
}
Map<String, String> operateNamespaceItems = getNamespaceItems(namespace); Map<String, String> operateNamespaceItems = getNamespaceItems(namespace);
...@@ -165,7 +160,7 @@ public class ReleaseService { ...@@ -165,7 +160,7 @@ public class ReleaseService {
//branch release //branch release
if (parentNamespace != null) { if (parentNamespace != null) {
return publishBranchNamespace(parentNamespace, namespace, operateNamespaceItems, return publishBranchNamespace(parentNamespace, namespace, operateNamespaceItems,
releaseName, releaseComment, operator); releaseName, releaseComment, operator, isEmergencyPublish);
} }
Namespace childNamespace = namespaceService.findChildNamespace(namespace); Namespace childNamespace = namespaceService.findChildNamespace(namespace);
...@@ -176,31 +171,43 @@ public class ReleaseService { ...@@ -176,31 +171,43 @@ public class ReleaseService {
} }
//master release //master release
Map<String, Object> operationContext = Maps.newHashMap();
operationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);
Release release = masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems, Release release = masterRelease(namespace, releaseName, releaseComment, operateNamespaceItems,
operator, ReleaseOperation.NORMAL_RELEASE, null); operator, ReleaseOperation.NORMAL_RELEASE, operationContext);
//merge to branch and auto release //merge to branch and auto release
if (childNamespace != null) { if (childNamespace != null) {
mergeFromMasterAndPublishBranch(namespace, childNamespace, operateNamespaceItems, mergeFromMasterAndPublishBranch(namespace, childNamespace, operateNamespaceItems,
releaseName, releaseComment, operator, previousRelease, release); releaseName, releaseComment, operator, previousRelease,
release, isEmergencyPublish);
} }
return release; return release;
} }
private void checkLock(Namespace namespace, boolean isEmergencyPublish, String operator) {
if (!isEmergencyPublish) {
NamespaceLock lock = namespaceLockService.findLock(namespace.getId());
if (lock != null && lock.getDataChangeCreatedBy().equals(operator)) {
throw new BadRequestException("Config can not be published by yourself.");
}
}
}
private void mergeFromMasterAndPublishBranch(Namespace parentNamespace, Namespace childNamespace, private void mergeFromMasterAndPublishBranch(Namespace parentNamespace, Namespace childNamespace,
Map<String, String> parentNamespaceItems, Map<String, String> parentNamespaceItems,
String releaseName, String releaseComment, String releaseName, String releaseComment,
String operator, Release masterPreviousRelease, Release parentRelease) { String operator, Release masterPreviousRelease,
// //create release for child namespace Release parentRelease, boolean isEmergencyPublish) {
//create release for child namespace
Map<String, String> childReleaseConfiguration = getNamespaceReleaseConfiguration(childNamespace); Map<String, String> childReleaseConfiguration = getNamespaceReleaseConfiguration(childNamespace);
Map<String, String> parentNamespaceOldConfiguration = masterPreviousRelease == null ? null : Map<String, String> parentNamespaceOldConfiguration = masterPreviousRelease == null ?
gson.fromJson( null : gson.fromJson(masterPreviousRelease.getConfigurations(),
masterPreviousRelease.getConfigurations(), GsonType.CONFIG);
configurationTypeReference);
Map<String, String> Map<String, String> childNamespaceToPublishConfigs =
childNamespaceToPublishConfigs =
calculateChildNamespaceToPublishConfiguration(parentNamespaceOldConfiguration, calculateChildNamespaceToPublishConfiguration(parentNamespaceOldConfiguration,
parentNamespaceItems, parentNamespaceItems,
childNamespace); childNamespace);
...@@ -208,27 +215,27 @@ public class ReleaseService { ...@@ -208,27 +215,27 @@ public class ReleaseService {
if (!childNamespaceToPublishConfigs.equals(childReleaseConfiguration)) { if (!childNamespaceToPublishConfigs.equals(childReleaseConfiguration)) {
branchRelease(parentNamespace, childNamespace, releaseName, releaseComment, branchRelease(parentNamespace, childNamespace, releaseName, releaseComment,
childNamespaceToPublishConfigs, parentRelease.getId(), operator, childNamespaceToPublishConfigs, parentRelease.getId(), operator,
ReleaseOperation.MASTER_NORMAL_RELEASE_MERGE_TO_GRAY); ReleaseOperation.MASTER_NORMAL_RELEASE_MERGE_TO_GRAY, isEmergencyPublish);
} }
} }
private Release publishBranchNamespace(Namespace parentNamespace, Namespace childNamespace, private Release publishBranchNamespace(Namespace parentNamespace, Namespace childNamespace,
Map<String, String> childNamespaceItems, Map<String, String> childNamespaceItems,
String releaseName, String releaseComment, String operator) { String releaseName, String releaseComment,
String operator, boolean isEmergencyPublish) {
Release parentLatestRelease = findLatestActiveRelease(parentNamespace); Release parentLatestRelease = findLatestActiveRelease(parentNamespace);
Map<String, String> parentConfigurations = parentLatestRelease != null ? Map<String, String> parentConfigurations = parentLatestRelease != null ?
gson.fromJson(parentLatestRelease.getConfigurations(), gson.fromJson(parentLatestRelease.getConfigurations(),
configurationTypeReference) : new HashMap<>(); GsonType.CONFIG) : new HashMap<>();
long baseReleaseId = parentLatestRelease == null ? 0 : parentLatestRelease.getId(); long baseReleaseId = parentLatestRelease == null ? 0 : parentLatestRelease.getId();
Map<String, String> childNamespaceToPublishConfigs = mergeConfiguration(parentConfigurations, childNamespaceItems); Map<String, String> childNamespaceToPublishConfigs = mergeConfiguration(parentConfigurations, childNamespaceItems);
Release release =
branchRelease(parentNamespace, childNamespace, releaseName, releaseComment,
childNamespaceToPublishConfigs, baseReleaseId, operator,
ReleaseOperation.GRAY_RELEASE);
return release; return branchRelease(parentNamespace, childNamespace, releaseName, releaseComment,
childNamespaceToPublishConfigs, baseReleaseId, operator,
ReleaseOperation.GRAY_RELEASE, isEmergencyPublish);
} }
private Release masterRelease(Namespace namespace, String releaseName, String releaseComment, private Release masterRelease(Namespace namespace, String releaseName, String releaseComment,
...@@ -241,8 +248,8 @@ public class ReleaseService { ...@@ -241,8 +248,8 @@ public class ReleaseService {
releaseHistoryService.createReleaseHistory(namespace.getAppId(), namespace.getClusterName(), releaseHistoryService.createReleaseHistory(namespace.getAppId(), namespace.getClusterName(),
namespace.getNamespaceName(), namespace.getClusterName(), namespace.getNamespaceName(), namespace.getClusterName(),
release.getId(), release.getId(), previousReleaseId, releaseOperation,
previousReleaseId, releaseOperation, operationContext, operator); operationContext, operator);
return release; return release;
} }
...@@ -250,7 +257,7 @@ public class ReleaseService { ...@@ -250,7 +257,7 @@ public class ReleaseService {
private Release branchRelease(Namespace parentNamespace, Namespace childNamespace, private Release branchRelease(Namespace parentNamespace, Namespace childNamespace,
String releaseName, String releaseComment, String releaseName, String releaseComment,
Map<String, String> configurations, long baseReleaseId, Map<String, String> configurations, long baseReleaseId,
String operator, int releaseOperation) { String operator, int releaseOperation, boolean isEmergencyPublish) {
Release previousRelease = findLatestActiveRelease(childNamespace.getAppId(), Release previousRelease = findLatestActiveRelease(childNamespace.getAppId(),
childNamespace.getClusterName(), childNamespace.getClusterName(),
childNamespace.getNamespaceName()); childNamespace.getNamespaceName());
...@@ -258,6 +265,7 @@ public class ReleaseService { ...@@ -258,6 +265,7 @@ public class ReleaseService {
Map<String, Object> releaseOperationContext = Maps.newHashMap(); Map<String, Object> releaseOperationContext = Maps.newHashMap();
releaseOperationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, baseReleaseId); releaseOperationContext.put(ReleaseOperationContext.BASE_RELEASE_ID, baseReleaseId);
releaseOperationContext.put(ReleaseOperationContext.IS_EMERGENCY_PUBLISH, isEmergencyPublish);
Release release = Release release =
createRelease(childNamespace, releaseName, releaseComment, configurations, operator); createRelease(childNamespace, releaseName, releaseComment, configurations, operator);
...@@ -316,8 +324,7 @@ public class ReleaseService { ...@@ -316,8 +324,7 @@ public class ReleaseService {
Release release = findLatestActiveRelease(namespace); Release release = findLatestActiveRelease(namespace);
Map<String, String> configuration = new HashMap<>(); Map<String, String> configuration = new HashMap<>();
if (release != null) { if (release != null) {
configuration = new Gson().fromJson(release.getConfigurations(), configuration = new Gson().fromJson(release.getConfigurations(), GsonType.CONFIG);
configurationTypeReference);
} }
return configuration; return configuration;
} }
...@@ -395,12 +402,11 @@ public class ReleaseService { ...@@ -395,12 +402,11 @@ public class ReleaseService {
Release parentNamespaceNewLatestRelease = parentNamespaceTwoLatestActiveRelease.get(1); Release parentNamespaceNewLatestRelease = parentNamespaceTwoLatestActiveRelease.get(1);
Map<String, String> parentNamespaceAbandonedConfiguration = gson.fromJson(abandonedRelease.getConfigurations(), Map<String, String> parentNamespaceAbandonedConfiguration = gson.fromJson(abandonedRelease.getConfigurations(),
configurationTypeReference); GsonType.CONFIG);
Map<String, String> Map<String, String>
parentNamespaceNewLatestConfiguration = parentNamespaceNewLatestConfiguration =
gson.fromJson(parentNamespaceNewLatestRelease.getConfigurations(), gson.fromJson(parentNamespaceNewLatestRelease.getConfigurations(), GsonType.CONFIG);
configurationTypeReference);
Map<String, String> Map<String, String>
childNamespaceNewConfiguration = childNamespaceNewConfiguration =
...@@ -408,9 +414,10 @@ public class ReleaseService { ...@@ -408,9 +414,10 @@ public class ReleaseService {
parentNamespaceNewLatestConfiguration, parentNamespaceNewLatestConfiguration,
childNamespace); childNamespace);
branchRelease(parentNamespace, childNamespace, TIMESTAMP_FORMAT.format(new Date()) + "-master-rollback-merge-to-gray", "", branchRelease(parentNamespace, childNamespace,
TIMESTAMP_FORMAT.format(new Date()) + "-master-rollback-merge-to-gray", "",
childNamespaceNewConfiguration, parentNamespaceNewLatestRelease.getId(), operator, childNamespaceNewConfiguration, parentNamespaceNewLatestRelease.getId(), operator,
ReleaseOperation.MATER_ROLLBACK_MERGE_TO_GRAY); ReleaseOperation.MATER_ROLLBACK_MERGE_TO_GRAY, false);
} }
private Map<String, String> calculateChildNamespaceToPublishConfiguration( private Map<String, String> calculateChildNamespaceToPublishConfiguration(
...@@ -423,7 +430,7 @@ public class ReleaseService { ...@@ -423,7 +430,7 @@ public class ReleaseService {
Map<String, String> childNamespaceLatestActiveConfiguration = childNamespaceLatestActiveRelease == null ? null : Map<String, String> childNamespaceLatestActiveConfiguration = childNamespaceLatestActiveRelease == null ? null :
gson.fromJson(childNamespaceLatestActiveRelease gson.fromJson(childNamespaceLatestActiveRelease
.getConfigurations(), .getConfigurations(),
configurationTypeReference); GsonType.CONFIG);
Map<String, String> childNamespaceModifiedConfiguration = calculateBranchModifiedItemsAccordingToRelease( Map<String, String> childNamespaceModifiedConfiguration = calculateBranchModifiedItemsAccordingToRelease(
parentNamespaceOldConfiguration, childNamespaceLatestActiveConfiguration); parentNamespaceOldConfiguration, childNamespaceLatestActiveConfiguration);
......
...@@ -8,6 +8,7 @@ import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule; ...@@ -8,6 +8,7 @@ import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.Namespace; import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory; import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import com.ctrip.framework.apollo.common.constants.GsonType;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation; import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import org.junit.Assert; import org.junit.Assert;
...@@ -24,8 +25,6 @@ import java.util.Map; ...@@ -24,8 +25,6 @@ import java.util.Map;
public class ReleaseCreationTest extends AbstractIntegrationTest { public class ReleaseCreationTest extends AbstractIntegrationTest {
private Gson gson = new Gson(); private Gson gson = new Gson();
private Type configurationTypeReference = new TypeToken<Map<String, String>>() {
}.getType();
@Autowired @Autowired
private ReleaseService releaseService; private ReleaseService releaseService;
...@@ -47,7 +46,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -47,7 +46,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String clusterName = "only-master"; String clusterName = "only-master";
Namespace namespace = instanceNamespace(namespaceId, clusterName); Namespace namespace = instanceNamespace(namespaceId, clusterName);
releaseService.publish(namespace, "", "", operator); releaseService.publish(namespace, "", "", operator, false);
Release latestRelease = releaseService.findLatestActiveRelease(namespace); Release latestRelease = releaseService.findLatestActiveRelease(namespace);
...@@ -92,7 +91,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -92,7 +91,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String childClusterName = "child-cluster1"; String childClusterName = "child-cluster1";
Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName); Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName);
releaseService.publish(parentNamespace, "", "", operator); releaseService.publish(parentNamespace, "", "", operator, false);
Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace); Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace);
...@@ -166,7 +165,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -166,7 +165,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String childClusterName = "child-cluster2"; String childClusterName = "child-cluster2";
Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName); Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName);
releaseService.publish(parentNamespace, "", "", operator); releaseService.publish(parentNamespace, "", "", operator, false);
Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace); Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace);
...@@ -237,7 +236,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -237,7 +236,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String childClusterName = "child-cluster3"; String childClusterName = "child-cluster3";
Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName); Namespace parentNamespace = instanceNamespace(parentNamespaceId, parentClusterName);
releaseService.publish(parentNamespace, "", "", operator); releaseService.publish(parentNamespace, "", "", operator, false);
Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace); Release latestParentNamespaceRelease = releaseService.findLatestActiveRelease(parentNamespace);
...@@ -306,7 +305,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -306,7 +305,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String childClusterName = "child-cluster4"; String childClusterName = "child-cluster4";
//assert child namespace //assert child namespace
Namespace childNamespace = instanceNamespace(childNamespaceId, childClusterName); Namespace childNamespace = instanceNamespace(childNamespaceId, childClusterName);
releaseService.publish(childNamespace, "", "", operator); releaseService.publish(childNamespace, "", "", operator, false);
Release latestChildNamespaceRelease = releaseService.findLatestActiveRelease(childNamespace); Release latestChildNamespaceRelease = releaseService.findLatestActiveRelease(childNamespace);
...@@ -377,7 +376,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -377,7 +376,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
String childClusterName = "child-cluster5"; String childClusterName = "child-cluster5";
//assert child namespace //assert child namespace
Namespace childNamespace = instanceNamespace(childNamespaceId, childClusterName); Namespace childNamespace = instanceNamespace(childNamespaceId, childClusterName);
releaseService.publish(childNamespace, "", "", operator); releaseService.publish(childNamespace, "", "", operator, false);
Release latestChildNamespaceRelease = releaseService.findLatestActiveRelease(childNamespace); Release latestChildNamespaceRelease = releaseService.findLatestActiveRelease(childNamespace);
...@@ -495,7 +494,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest { ...@@ -495,7 +494,7 @@ public class ReleaseCreationTest extends AbstractIntegrationTest {
} }
private Map<String, String> parseConfiguration(String configuration) { private Map<String, String> parseConfiguration(String configuration) {
return gson.fromJson(configuration, configurationTypeReference); return gson.fromJson(configuration, GsonType.CONFIG);
} }
} }
...@@ -8,4 +8,5 @@ public interface ReleaseOperationContext { ...@@ -8,4 +8,5 @@ public interface ReleaseOperationContext {
String RULES = "rules"; String RULES = "rules";
String OLD_RULES = "oldRules"; String OLD_RULES = "oldRules";
String BASE_RELEASE_ID = "baseReleaseId"; String BASE_RELEASE_ID = "baseReleaseId";
String IS_EMERGENCY_PUBLISH = "isEmergencyPublish";
} }
...@@ -230,31 +230,32 @@ public class AdminServiceAPI { ...@@ -230,31 +230,32 @@ public class AdminServiceAPI {
} }
public ReleaseDTO createRelease(String appId, Env env, String clusterName, String namespace, public ReleaseDTO createRelease(String appId, Env env, String clusterName, String namespace,
String releaseName, String releaseComment, String operator) { String releaseName, String releaseComment, String operator, boolean isEmergencyPublish) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8")); headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("name", releaseName); parameters.add("name", releaseName);
parameters.add("comment", releaseComment); parameters.add("comment", releaseComment);
parameters.add("operator", operator); parameters.add("operator", operator);
parameters.add("isEmergencyPublish", String.valueOf(isEmergencyPublish));
HttpEntity<MultiValueMap<String, String>> entity = HttpEntity<MultiValueMap<String, String>> entity =
new HttpEntity<>(parameters, headers); new HttpEntity<>(parameters, headers);
ReleaseDTO response = restTemplate.post( ReleaseDTO response = restTemplate.post(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity, env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity,
ReleaseDTO.class, ReleaseDTO.class, appId, clusterName, namespace);
appId, clusterName, namespace);
return response; return response;
} }
public ReleaseDTO updateAndPublish(String appId, Env env, String clusterName, String namespace, public ReleaseDTO updateAndPublish(String appId, Env env, String clusterName, String namespace,
String releaseName, String releaseComment, String branchName, String releaseName, String releaseComment, String branchName,
boolean deleteBranch, ItemChangeSets changeSets) { boolean isEmergencyPublish, boolean deleteBranch, ItemChangeSets changeSets) {
return restTemplate.post(env, return restTemplate.post(env,
"apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish?" "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish?"
+ "releaseName={releaseName}&releaseComment={releaseComment}&branchName={branchName}&deleteBranch={deleteBranch}", + "releaseName={releaseName}&releaseComment={releaseComment}&branchName={branchName}"
changeSets, ReleaseDTO.class, + "&deleteBranch={deleteBranch}&isEmergencyPublish={isEmergencyPublish}",
appId, clusterName, namespace, releaseName, releaseComment, branchName, deleteBranch); changeSets, ReleaseDTO.class, appId, clusterName, namespace,
releaseName, releaseComment, branchName, deleteBranch, isEmergencyPublish);
} }
......
...@@ -20,6 +20,7 @@ import org.springframework.util.StringUtils; ...@@ -20,6 +20,7 @@ import org.springframework.util.StringUtils;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
@Component @Component
...@@ -97,6 +98,19 @@ public class PortalConfig extends RefreshableConfig { ...@@ -97,6 +98,19 @@ public class PortalConfig extends RefreshableConfig {
return getValue("apollo.portal.address"); return getValue("apollo.portal.address");
} }
public boolean isEmergencyPublishAllowed(Env env) {
String targetEnv = env.name();
String[] emergencyPublishSupportedEnvs = getArrayProperty("emergencyPublish.supported.envs", new String[0]);
for (String supportedEnv: emergencyPublishSupportedEnvs) {
if (Objects.equals(targetEnv, supportedEnv.toUpperCase().trim())) {
return true;
}
}
return false;
}
/*** /***
* Level: low * Level: low
......
...@@ -4,6 +4,7 @@ package com.ctrip.framework.apollo.portal.components.emailbuilder; ...@@ -4,6 +4,7 @@ package com.ctrip.framework.apollo.portal.components.emailbuilder;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation; import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import com.ctrip.framework.apollo.common.constants.ReleaseOperationContext;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
...@@ -29,12 +30,15 @@ import java.util.ArrayList; ...@@ -29,12 +30,15 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
public abstract class ConfigPublishEmailBuilder { public abstract class ConfigPublishEmailBuilder {
private static final String EMERGENCY_PUBLISH_TAG = "<span style='color:red'>(紧急发布)</span>";
//email content common field placeholder //email content common field placeholder
private static final String EMAIL_CONTENT_FIELD_APPID = "#\\{appId\\}"; private static final String EMAIL_CONTENT_FIELD_APPID = "#\\{appId\\}";
private static final String EMAIL_CONTENT_FIELD_ENV = "#\\{env}"; private static final String EMAIL_CONTENT_FIELD_ENV = "#\\{env}";
...@@ -48,6 +52,7 @@ public abstract class ConfigPublishEmailBuilder { ...@@ -48,6 +52,7 @@ public abstract class ConfigPublishEmailBuilder {
private static final String EMAIL_CONTENT_FIELD_RELEASE_COMMENT = "#\\{releaseComment}"; private static final String EMAIL_CONTENT_FIELD_RELEASE_COMMENT = "#\\{releaseComment}";
private static final String EMAIL_CONTENT_FIELD_APOLLO_SERVER_ADDRESS = "#\\{apollo.portal.address}"; private static final String EMAIL_CONTENT_FIELD_APOLLO_SERVER_ADDRESS = "#\\{apollo.portal.address}";
private static final String EMAIL_CONTENT_FIELD_DIFF_CONTENT = "#\\{diffContent}"; private static final String EMAIL_CONTENT_FIELD_DIFF_CONTENT = "#\\{diffContent}";
private static final String EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH = "#\\{emergencyPublish}";
private static final String EMAIL_CONTENT_DIFF_MODULE = "#\\{diffModule}"; private static final String EMAIL_CONTENT_DIFF_MODULE = "#\\{diffModule}";
protected static final String EMAIL_CONTENT_GRAY_RULES_MODULE = "#\\{rulesModule}"; protected static final String EMAIL_CONTENT_GRAY_RULES_MODULE = "#\\{rulesModule}";
...@@ -118,7 +123,18 @@ public abstract class ConfigPublishEmailBuilder { ...@@ -118,7 +123,18 @@ public abstract class ConfigPublishEmailBuilder {
} }
private String renderReleaseBasicInfo(String template, Env env, ReleaseHistoryBO releaseHistory) { private String renderReleaseBasicInfo(String template, Env env, ReleaseHistoryBO releaseHistory) {
String renderResult = template.replaceAll(EMAIL_CONTENT_FIELD_APPID, Matcher.quoteReplacement(releaseHistory.getAppId())); String renderResult = template;
Map<String, Object> operationContext = releaseHistory.getOperationContext();
boolean isEmergencyPublish = operationContext.containsKey(ReleaseOperationContext.IS_EMERGENCY_PUBLISH) &&
(boolean) operationContext.get(ReleaseOperationContext.IS_EMERGENCY_PUBLISH);
if (isEmergencyPublish) {
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH, Matcher.quoteReplacement(EMERGENCY_PUBLISH_TAG));
} else {
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_EMERGENCY_PUBLISH, "");
}
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_APPID, Matcher.quoteReplacement(releaseHistory.getAppId()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_ENV, Matcher.quoteReplacement(env.toString())); renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_ENV, Matcher.quoteReplacement(env.toString()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_CLUSTER, Matcher.quoteReplacement(releaseHistory.getClusterName())); renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_CLUSTER, Matcher.quoteReplacement(releaseHistory.getClusterName()));
renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_NAMESPACE, Matcher.quoteReplacement(releaseHistory.getNamespaceName())); renderResult = renderResult.replaceAll(EMAIL_CONTENT_FIELD_NAMESPACE, Matcher.quoteReplacement(releaseHistory.getNamespaceName()));
......
...@@ -3,8 +3,10 @@ package com.ctrip.framework.apollo.portal.controller; ...@@ -3,8 +3,10 @@ package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleDTO; import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.components.PermissionValidator; import com.ctrip.framework.apollo.portal.components.PermissionValidator;
import com.ctrip.framework.apollo.portal.components.config.PortalConfig;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO; import com.ctrip.framework.apollo.portal.entity.bo.NamespaceBO;
import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent; import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent;
...@@ -33,6 +35,8 @@ public class NamespaceBranchController { ...@@ -33,6 +35,8 @@ public class NamespaceBranchController {
private NamespaceBranchService namespaceBranchService; private NamespaceBranchService namespaceBranchService;
@Autowired @Autowired
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
@Autowired
private PortalConfig portalConfig;
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches") @RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches")
public NamespaceBO findBranch(@PathVariable String appId, public NamespaceBO findBranch(@PathVariable String appId,
...@@ -84,8 +88,13 @@ public class NamespaceBranchController { ...@@ -84,8 +88,13 @@ public class NamespaceBranchController {
@PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch, @PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,
@RequestBody NamespaceReleaseModel model) { @RequestBody NamespaceReleaseModel model) {
if (model.isEmergencyPublish() && !portalConfig.isEmergencyPublishAllowed(Env.fromString(env))) {
throw new BadRequestException(String.format("Env: %s is not supported emergency publish now", env));
}
ReleaseDTO createdRelease = namespaceBranchService.merge(appId, Env.valueOf(env), clusterName, namespaceName, branchName, ReleaseDTO createdRelease = namespaceBranchService.merge(appId, Env.valueOf(env), clusterName, namespaceName, branchName,
model.getReleaseTitle(), model.getReleaseComment(), deleteBranch); model.getReleaseTitle(), model.getReleaseComment(),
model.isEmergencyPublish(), deleteBranch);
ConfigPublishEvent event = ConfigPublishEvent.instance(); ConfigPublishEvent event = ConfigPublishEvent.instance();
event.withAppId(appId) event.withAppId(appId)
......
...@@ -2,6 +2,7 @@ package com.ctrip.framework.apollo.portal.controller; ...@@ -2,6 +2,7 @@ package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO; import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.entity.vo.LockInfo;
import com.ctrip.framework.apollo.portal.service.NamespaceLockService; import com.ctrip.framework.apollo.portal.service.NamespaceLockService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -15,6 +16,7 @@ public class NamespaceLockController { ...@@ -15,6 +16,7 @@ public class NamespaceLockController {
@Autowired @Autowired
private NamespaceLockService namespaceLockService; private NamespaceLockService namespaceLockService;
@Deprecated
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/lock") @RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/lock")
public NamespaceLockDTO getNamespaceLock(@PathVariable String appId, @PathVariable String env, public NamespaceLockDTO getNamespaceLock(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName) { @PathVariable String clusterName, @PathVariable String namespaceName) {
...@@ -22,4 +24,13 @@ public class NamespaceLockController { ...@@ -22,4 +24,13 @@ public class NamespaceLockController {
return namespaceLockService.getNamespaceLock(appId, Env.valueOf(env), clusterName, namespaceName); return namespaceLockService.getNamespaceLock(appId, Env.valueOf(env), clusterName, namespaceName);
} }
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/lock-info")
public LockInfo getNamespaceLockInfo(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName) {
return namespaceLockService.getNamespaceLockInfo(appId, Env.fromString(env), clusterName, namespaceName);
}
} }
package com.ctrip.framework.apollo.portal.controller; package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition; import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.components.config.PortalConfig;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO; import com.ctrip.framework.apollo.portal.entity.bo.ReleaseBO;
...@@ -30,6 +32,8 @@ public class ReleaseController { ...@@ -30,6 +32,8 @@ public class ReleaseController {
private ReleaseService releaseService; private ReleaseService releaseService;
@Autowired @Autowired
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
@Autowired
private PortalConfig portalConfig;
@PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)") @PreAuthorize(value = "@permissionValidator.hasReleaseNamespacePermission(#appId, #namespaceName)")
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST) @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
...@@ -43,6 +47,10 @@ public class ReleaseController { ...@@ -43,6 +47,10 @@ public class ReleaseController {
model.setClusterName(clusterName); model.setClusterName(clusterName);
model.setNamespaceName(namespaceName); model.setNamespaceName(namespaceName);
if (model.isEmergencyPublish() && !portalConfig.isEmergencyPublishAllowed(Env.valueOf(env))) {
throw new BadRequestException(String.format("Env: %s is not supported emergency publish now", env));
}
ReleaseDTO createdRelease = releaseService.publish(model); ReleaseDTO createdRelease = releaseService.publish(model);
ConfigPublishEvent event = ConfigPublishEvent.instance(); ConfigPublishEvent event = ConfigPublishEvent.instance();
...@@ -72,6 +80,10 @@ public class ReleaseController { ...@@ -72,6 +80,10 @@ public class ReleaseController {
model.setClusterName(branchName); model.setClusterName(branchName);
model.setNamespaceName(namespaceName); model.setNamespaceName(namespaceName);
if (model.isEmergencyPublish() && !portalConfig.isEmergencyPublishAllowed(Env.valueOf(env))) {
throw new BadRequestException(String.format("Env: %s is not supported emergency publish now", env));
}
ReleaseDTO createdRelease = releaseService.publish(model); ReleaseDTO createdRelease = releaseService.publish(model);
ConfigPublishEvent event = ConfigPublishEvent.instance(); ConfigPublishEvent event = ConfigPublishEvent.instance();
......
...@@ -13,6 +13,7 @@ public class NamespaceReleaseModel implements Verifiable { ...@@ -13,6 +13,7 @@ public class NamespaceReleaseModel implements Verifiable {
private String releaseTitle; private String releaseTitle;
private String releaseComment; private String releaseComment;
private String releasedBy; private String releasedBy;
private boolean isEmergencyPublish;
@Override @Override
public boolean isInvalid() { public boolean isInvalid() {
...@@ -74,4 +75,12 @@ public class NamespaceReleaseModel implements Verifiable { ...@@ -74,4 +75,12 @@ public class NamespaceReleaseModel implements Verifiable {
public void setReleasedBy(String releasedBy) { public void setReleasedBy(String releasedBy) {
this.releasedBy = releasedBy; this.releasedBy = releasedBy;
} }
public boolean isEmergencyPublish() {
return isEmergencyPublish;
}
public void setEmergencyPublish(boolean emergencyPublish) {
isEmergencyPublish = emergencyPublish;
}
} }
package com.ctrip.framework.apollo.portal.entity.vo;
public class LockInfo {
private String lockOwner;
private boolean isEmergencyPublishAllowed;
public String getLockOwner() {
return lockOwner;
}
public void setLockOwner(String lockOwner) {
this.lockOwner = lockOwner;
}
public boolean isEmergencyPublishAllowed() {
return isEmergencyPublishAllowed;
}
public void setEmergencyPublishAllowed(boolean emergencyPublishAllowed) {
isEmergencyPublishAllowed = emergencyPublishAllowed;
}
}
...@@ -84,13 +84,14 @@ public class NamespaceBranchService { ...@@ -84,13 +84,14 @@ public class NamespaceBranchService {
public ReleaseDTO merge(String appId, Env env, String clusterName, String namespaceName, public ReleaseDTO merge(String appId, Env env, String clusterName, String namespaceName,
String branchName, String title, String comment, boolean deleteBranch) { String branchName, String title, String comment,
boolean isEmergencyPublish, boolean deleteBranch) {
ItemChangeSets changeSets = calculateBranchChangeSet(appId, env, clusterName, namespaceName, branchName); ItemChangeSets changeSets = calculateBranchChangeSet(appId, env, clusterName, namespaceName, branchName);
ReleaseDTO ReleaseDTO mergedResult =
mergedResult = releaseService.updateAndPublish(appId, env, clusterName, namespaceName, title, comment,
releaseService.updateAndPublish(appId, env, clusterName, namespaceName, title, comment, branchName, deleteBranch, changeSets); branchName, isEmergencyPublish, deleteBranch, changeSets);
Tracer.logEvent(CatEventType.MERGE_GRAY_RELEASE, Tracer.logEvent(CatEventType.MERGE_GRAY_RELEASE,
String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
......
...@@ -3,6 +3,8 @@ package com.ctrip.framework.apollo.portal.service; ...@@ -3,6 +3,8 @@ package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO; import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.components.config.PortalConfig;
import com.ctrip.framework.apollo.portal.entity.vo.LockInfo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -12,10 +14,24 @@ public class NamespaceLockService { ...@@ -12,10 +14,24 @@ public class NamespaceLockService {
@Autowired @Autowired
private AdminServiceAPI.NamespaceLockAPI namespaceLockAPI; private AdminServiceAPI.NamespaceLockAPI namespaceLockAPI;
@Autowired
private PortalConfig portalConfig;
public NamespaceLockDTO getNamespaceLock(String appId, Env env, String clusterName, String namespaceName) { public NamespaceLockDTO getNamespaceLock(String appId, Env env, String clusterName, String namespaceName) {
return namespaceLockAPI.getNamespaceLockOwner(appId, env, clusterName, namespaceName); return namespaceLockAPI.getNamespaceLockOwner(appId, env, clusterName, namespaceName);
}
public LockInfo getNamespaceLockInfo(String appId, Env env, String clusterName, String namespaceName) {
LockInfo lockInfo = new LockInfo();
NamespaceLockDTO namespaceLockDTO = namespaceLockAPI.getNamespaceLockOwner(appId, env, clusterName, namespaceName);
String lockOwner = namespaceLockDTO == null ? "" : namespaceLockDTO.getDataChangeCreatedBy();
lockInfo.setLockOwner(lockOwner);
lockInfo.setEmergencyPublishAllowed(portalConfig.isEmergencyPublishAllowed(env));
return lockInfo;
} }
} }
...@@ -42,27 +42,30 @@ public class ReleaseService { ...@@ -42,27 +42,30 @@ public class ReleaseService {
private AdminServiceAPI.ReleaseAPI releaseAPI; private AdminServiceAPI.ReleaseAPI releaseAPI;
public ReleaseDTO publish(NamespaceReleaseModel model) { public ReleaseDTO publish(NamespaceReleaseModel model) {
String appId = model.getAppId();
Env env = model.getEnv(); Env env = model.getEnv();
boolean isEmergencyPublish = model.isEmergencyPublish();
String appId = model.getAppId();
String clusterName = model.getClusterName(); String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName(); String namespaceName = model.getNamespaceName();
String releaseBy = String releaseBy = StringUtils.isEmpty(model.getReleasedBy()) ?
StringUtils.isEmpty(model.getReleasedBy()) ? userInfoHolder.getUser().getUserId() : model.getReleasedBy(); userInfoHolder.getUser().getUserId() : model.getReleasedBy();
ReleaseDTO releaseDTO = releaseAPI.createRelease(appId, env, clusterName, namespaceName,
model.getReleaseTitle(), model.getReleaseComment(),
releaseBy, isEmergencyPublish);
ReleaseDTO releaseDTO = releaseAPI Tracer.logEvent(CatEventType.RELEASE_NAMESPACE,
.createRelease(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment() String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
, releaseBy);
Tracer.logEvent(CatEventType.RELEASE_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
return releaseDTO; return releaseDTO;
} }
public ReleaseDTO updateAndPublish(String appId, Env env, String clusterName, String namespaceName, public ReleaseDTO updateAndPublish(String appId, Env env, String clusterName, String namespaceName,
String releaseTitle, String releaseComment, String branchName, String releaseTitle, String releaseComment, String branchName,
boolean deleteBranch, ItemChangeSets changeSets) { boolean isEmergencyPublish, boolean deleteBranch, ItemChangeSets changeSets) {
return releaseAPI.updateAndPublish(appId, env, clusterName, namespaceName, releaseTitle, releaseComment, branchName, return releaseAPI.updateAndPublish(appId, env, clusterName, namespaceName, releaseTitle, releaseComment, branchName,
deleteBranch, changeSets); isEmergencyPublish, deleteBranch, changeSets);
} }
public List<ReleaseBO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page, public List<ReleaseBO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
......
...@@ -229,6 +229,7 @@ ...@@ -229,6 +229,7 @@
cluster="pageContext.clusterName"> cluster="pageContext.clusterName">
</mergeandpublishmodal> </mergeandpublishmodal>
<publishdenymodal></publishdenymodal>
<apolloconfirmdialog apollo-dialog-id="'deleteConfirmDialog'" apollo-title="'删除配置'" <apolloconfirmdialog apollo-dialog-id="'deleteConfirmDialog'" apollo-title="'删除配置'"
apollo-detail="'确定要删除配置吗?'" apollo-detail="'确定要删除配置吗?'"
...@@ -251,10 +252,6 @@ ...@@ -251,10 +252,6 @@
apollo-detail="'当前namespace正在被 ' + lockOwner + ' 编辑,一次发布只能被一个人修改.'" apollo-detail="'当前namespace正在被 ' + lockOwner + ' 编辑,一次发布只能被一个人修改.'"
apollo-show-cancel-btn="false"></apolloconfirmdialog> apollo-show-cancel-btn="false"></apolloconfirmdialog>
<apolloconfirmdialog apollo-dialog-id="'releaseDenyDialog'" apollo-title="'发布受限'"
apollo-detail="'您不能发布哟~ ' + pageContext.env + '环境配置的编辑和发布必须为不同的人,请找另一个具有当前namespace发布权的人操作发布~'"
apollo-show-cancel-btn="false"></apolloconfirmdialog>
<apolloconfirmdialog apollo-dialog-id="'rollbackAlertDialog'" apollo-title="'回滚'" <apolloconfirmdialog apollo-dialog-id="'rollbackAlertDialog'" apollo-title="'回滚'"
apollo-detail="'确定要回滚吗?'" apollo-detail="'确定要回滚吗?'"
apollo-show-cancel-btn="true" apollo-confirm="rollback"></apolloconfirmdialog> apollo-show-cancel-btn="true" apollo-confirm="rollback"></apolloconfirmdialog>
...@@ -373,6 +370,7 @@ ...@@ -373,6 +370,7 @@
<script type="application/javascript" src="scripts/directive/rollback-modal-directive.js"></script> <script type="application/javascript" src="scripts/directive/rollback-modal-directive.js"></script>
<script type="application/javascript" src="scripts/directive/gray-release-rules-modal-directive.js"></script> <script type="application/javascript" src="scripts/directive/gray-release-rules-modal-directive.js"></script>
<script type="application/javascript" src="scripts/directive/merge-and-publish-modal-directive.js"></script> <script type="application/javascript" src="scripts/directive/merge-and-publish-modal-directive.js"></script>
<script type="application/javascript" src="scripts/directive/publish-deny-modal-directive.js"></script>
<!--controller--> <!--controller-->
<script type="application/javascript" src="scripts/controller/config/ConfigNamespaceController.js"></script> <script type="application/javascript" src="scripts/controller/config/ConfigNamespaceController.js"></script>
......
...@@ -75,7 +75,8 @@ ...@@ -75,7 +75,8 @@
<h5 class="col-md-7 word-break" ng-show="releaseHistory.operation == 8">删除灰度(全量发布)</h5> <h5 class="col-md-7 word-break" ng-show="releaseHistory.operation == 8">删除灰度(全量发布)</h5>
<h6 class="col-md-5 text-right" ng-bind="releaseHistory.releaseTimeFormatted"></h6> <h6 class="col-md-5 text-right" ng-bind="releaseHistory.releaseTimeFormatted"></h6>
<span class="label label-warning no-radius emergency-publish"
ng-if="releaseHistory.operationContext.isEmergencyPublish">紧急发布</span>
</div> </div>
</div> </div>
......
...@@ -20,17 +20,22 @@ function mergeAndPublishDirective(AppUtil, EventManager) { ...@@ -20,17 +20,22 @@ function mergeAndPublishDirective(AppUtil, EventManager) {
var branch = context.branch; var branch = context.branch;
scope.toReleaseNamespace = branch; scope.toReleaseNamespace = branch;
scope.toDeleteBranch = branch; scope.toDeleteBranch = branch;
scope.isEmergencyPublish =
context.isEmergencyPublish ? context.isEmergencyPublish : false;
var branchStatusMerge = 2; var branchStatusMerge = 2;
branch.branchStatus = branchStatusMerge; branch.branchStatus = branchStatusMerge;
branch.mergeAndPublish = true; branch.mergeAndPublish = true;
AppUtil.showModal('#mergeAndPublishModal'); AppUtil.showModal('#mergeAndPublishModal');
}); });
function showReleaseModal() { function showReleaseModal() {
EventManager.emit(EventManager.EventType.PUBLISH_NAMESPACE, {namespace: scope.toReleaseNamespace}); EventManager.emit(EventManager.EventType.PUBLISH_NAMESPACE,
{
namespace: scope.toReleaseNamespace,
isEmergencyPublish: scope.isEmergencyPublish
});
} }
} }
......
...@@ -279,16 +279,12 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -279,16 +279,12 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
} }
function initNamespaceLock(namespace) { function initNamespaceLock(namespace) {
NamespaceLockService.get_namespace_lock( NamespaceLockService.get_namespace_lock(scope.appId, scope.env,
scope.appId, scope.env, namespace.baseInfo.clusterName,
namespace.baseInfo.clusterName, namespace.baseInfo.namespaceName)
namespace.baseInfo.namespaceName)
.then(function (result) { .then(function (result) {
if (result.dataChangeCreatedBy) { namespace.lockOwner = result.lockOwner;
namespace.lockOwner = result.dataChangeCreatedBy; namespace.isEmergencyPublishAllowed = result.isEmergencyPublishAllowed;
} else {
namespace.lockOwner = "";
}
}); });
} }
...@@ -748,7 +744,10 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -748,7 +744,10 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
return; return;
} else if (namespace.lockOwner && scope.user == namespace.lockOwner) { } else if (namespace.lockOwner && scope.user == namespace.lockOwner) {
//can not publish if config modified by himself //can not publish if config modified by himself
AppUtil.showModal('#releaseDenyDialog'); EventManager.emit(EventManager.EventType.PUBLISH_DENY, {
namespace: namespace,
mergeAndPublish: false
});
return; return;
} }
...@@ -766,12 +765,14 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na ...@@ -766,12 +765,14 @@ function directive($window, toastr, AppUtil, EventManager, PermissionService, Na
var parentNamespace = branch.parentNamespace; var parentNamespace = branch.parentNamespace;
if (!parentNamespace.hasReleasePermission) { if (!parentNamespace.hasReleasePermission) {
AppUtil.showModal('#releaseNoPermissionDialog'); AppUtil.showModal('#releaseNoPermissionDialog');
} else if (branch.lockOwner && scope.user == branch.lockOwner) {
AppUtil.showModal('#releaseDenyDialog');
} else if (parentNamespace.itemModifiedCnt > 0) { } else if (parentNamespace.itemModifiedCnt > 0) {
AppUtil.showModal('#mergeAndReleaseDenyDialog'); AppUtil.showModal('#mergeAndReleaseDenyDialog');
} } else if (branch.lockOwner && scope.user == branch.lockOwner) {
else { EventManager.emit(EventManager.EventType.PUBLISH_DENY, {
namespace: branch,
mergeAndPublish: true
});
} else {
EventManager.emit(EventManager.EventType.MERGE_AND_PUBLISH_NAMESPACE, {branch: branch}); EventManager.emit(EventManager.EventType.MERGE_AND_PUBLISH_NAMESPACE, {branch: branch});
} }
} }
......
directive_module.directive('publishdenymodal', publishDenyDirective);
function publishDenyDirective(AppUtil, EventManager) {
return {
restrict: 'E',
templateUrl: '../../views/component/publish-deny-modal.html',
transclude: true,
replace: true,
scope: {
},
link: function (scope) {
var MODAL_ID = "#publishDenyModal";
EventManager.subscribe(EventManager.EventType.PUBLISH_DENY, function (context) {
scope.toReleaseNamespace = context.namespace;
scope.mergeAndPublish = !!context.mergeAndPublish;
AppUtil.showModal(MODAL_ID);
});
scope.emergencyPublish = emergencyPublish;
function emergencyPublish() {
AppUtil.hideModal(MODAL_ID);
if (scope.mergeAndPublish) {
EventManager.emit(EventManager.EventType.MERGE_AND_PUBLISH_NAMESPACE,
{
branch: scope.toReleaseNamespace,
isEmergencyPublish: true
});
} else {
EventManager.emit(EventManager.EventType.PUBLISH_NAMESPACE,
{
namespace: scope.toReleaseNamespace,
isEmergencyPublish: true
});
}
}
}
}
}
...@@ -19,12 +19,14 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na ...@@ -19,12 +19,14 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na
scope.releaseBtnDisabled = false; scope.releaseBtnDisabled = false;
scope.releaseChangeViewType = 'change'; scope.releaseChangeViewType = 'change';
scope.releaseComment = ''; scope.releaseComment = '';
scope.isEmergencyPublish = false;
EventManager.subscribe(EventManager.EventType.PUBLISH_NAMESPACE, EventManager.subscribe(EventManager.EventType.PUBLISH_NAMESPACE,
function (context) { function (context) {
var namespace = context.namespace; var namespace = context.namespace;
scope.toReleaseNamespace = context.namespace; scope.toReleaseNamespace = context.namespace;
scope.isEmergencyPublish = !!context.isEmergencyPublish;
var date = new Date().Format("yyyyMMddhhmmss"); var date = new Date().Format("yyyyMMddhhmmss");
if (namespace.mergeAndPublish) { if (namespace.mergeAndPublish) {
...@@ -55,7 +57,8 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na ...@@ -55,7 +57,8 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na
scope.toReleaseNamespace.baseInfo.clusterName, scope.toReleaseNamespace.baseInfo.clusterName,
scope.toReleaseNamespace.baseInfo.namespaceName, scope.toReleaseNamespace.baseInfo.namespaceName,
scope.toReleaseNamespace.releaseTitle, scope.toReleaseNamespace.releaseTitle,
scope.releaseComment).then( scope.releaseComment,
scope.isEmergencyPublish).then(
function (result) { function (result) {
AppUtil.hideModal('#releaseModal'); AppUtil.hideModal('#releaseModal');
toastr.success("发布成功"); toastr.success("发布成功");
...@@ -83,7 +86,8 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na ...@@ -83,7 +86,8 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na
scope.toReleaseNamespace.baseInfo.namespaceName, scope.toReleaseNamespace.baseInfo.namespaceName,
scope.toReleaseNamespace.baseInfo.clusterName, scope.toReleaseNamespace.baseInfo.clusterName,
scope.toReleaseNamespace.releaseTitle, scope.toReleaseNamespace.releaseTitle,
scope.releaseComment).then( scope.releaseComment,
scope.isEmergencyPublish).then(
function (result) { function (result) {
AppUtil.hideModal('#releaseModal'); AppUtil.hideModal('#releaseModal');
toastr.success("灰度发布成功"); toastr.success("灰度发布成功");
...@@ -98,7 +102,9 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na ...@@ -98,7 +102,9 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na
item.isModified = false; item.isModified = false;
} }
}); });
//reset namespace status
scope.toReleaseNamespace.itemModifiedCnt = 0; scope.toReleaseNamespace.itemModifiedCnt = 0;
scope.toReleaseNamespace.lockOwner = undefined;
//check rules //check rules
if (!scope.toReleaseNamespace.rules if (!scope.toReleaseNamespace.rules
...@@ -125,6 +131,7 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na ...@@ -125,6 +131,7 @@ function releaseModalDirective(toastr, AppUtil, EventManager, ReleaseService, Na
scope.toReleaseNamespace.baseInfo.clusterName, scope.toReleaseNamespace.baseInfo.clusterName,
scope.toReleaseNamespace.releaseTitle, scope.toReleaseNamespace.releaseTitle,
scope.releaseComment, scope.releaseComment,
scope.isEmergencyPublish,
scope.toReleaseNamespace.mergeAfterDeleteBranch) scope.toReleaseNamespace.mergeAfterDeleteBranch)
.then(function (result) { .then(function (result) {
......
...@@ -131,7 +131,8 @@ appService.service('EventManager', [function () { ...@@ -131,7 +131,8 @@ appService.service('EventManager', [function () {
PRE_ROLLBACK_NAMESPACE: 'pre_rollback_namespace', PRE_ROLLBACK_NAMESPACE: 'pre_rollback_namespace',
ROLLBACK_NAMESPACE: 'rollback_namespace', ROLLBACK_NAMESPACE: 'rollback_namespace',
EDIT_GRAY_RELEASE_RULES: 'edit_gray_release_rules', EDIT_GRAY_RELEASE_RULES: 'edit_gray_release_rules',
UPDATE_GRAY_RELEASE_RULES: 'update_gray_release_rules' UPDATE_GRAY_RELEASE_RULES: 'update_gray_release_rules',
PUBLISH_DENY: 'publish_deny'
} }
} }
......
...@@ -83,7 +83,7 @@ appService.service('NamespaceBranchService', ['$resource', '$q', function ($reso ...@@ -83,7 +83,7 @@ appService.service('NamespaceBranchService', ['$resource', '$q', function ($reso
} }
function merge_and_release_branch(appId, env, clusterName, namespaceName, function merge_and_release_branch(appId, env, clusterName, namespaceName,
branchName, title, comment, deleteBranch) { branchName, title, comment, isEmergencyPublish, deleteBranch) {
var d = $q.defer(); var d = $q.defer();
resource.merge_and_release_branch({ resource.merge_and_release_branch({
appId: appId, appId: appId,
...@@ -94,7 +94,8 @@ appService.service('NamespaceBranchService', ['$resource', '$q', function ($reso ...@@ -94,7 +94,8 @@ appService.service('NamespaceBranchService', ['$resource', '$q', function ($reso
deleteBranch:deleteBranch deleteBranch:deleteBranch
}, { }, {
releaseTitle: title, releaseTitle: title,
releaseComment: comment releaseComment: comment,
isEmergencyPublish: isEmergencyPublish
}, },
function (result) { function (result) {
d.resolve(result); d.resolve(result);
......
...@@ -2,7 +2,7 @@ appService.service('NamespaceLockService', ['$resource', '$q', function ($resour ...@@ -2,7 +2,7 @@ appService.service('NamespaceLockService', ['$resource', '$q', function ($resour
var resource = $resource('', {}, { var resource = $resource('', {}, {
get_namespace_lock: { get_namespace_lock: {
method: 'GET', method: 'GET',
url: 'apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/lock' url: 'apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/lock-info'
} }
}); });
......
...@@ -28,7 +28,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -28,7 +28,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
} }
}); });
function createRelease(appId, env, clusterName, namespaceName, releaseTitle, comment) { function createRelease(appId, env, clusterName, namespaceName, releaseTitle, comment, isEmergencyPublish) {
var d = $q.defer(); var d = $q.defer();
resource.release({ resource.release({
appId: appId, appId: appId,
...@@ -37,7 +37,8 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -37,7 +37,8 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
namespaceName: namespaceName namespaceName: namespaceName
}, { }, {
releaseTitle: releaseTitle, releaseTitle: releaseTitle,
releaseComment: comment releaseComment: comment,
isEmergencyPublish: isEmergencyPublish
}, function (result) { }, function (result) {
d.resolve(result); d.resolve(result);
}, function (result) { }, function (result) {
...@@ -46,18 +47,19 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -46,18 +47,19 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return d.promise; return d.promise;
} }
function createGrayRelease(appId, env, clusterName, namespaceName, branchName, releaseTitle, comment) { function createGrayRelease(appId, env, clusterName, namespaceName, branchName, releaseTitle, comment, isEmergencyPublish) {
var d = $q.defer(); var d = $q.defer();
resource.gray_release({ resource.gray_release({
appId: appId, appId: appId,
env: env, env: env,
clusterName: clusterName, clusterName: clusterName,
namespaceName: namespaceName, namespaceName: namespaceName,
branchName:branchName branchName: branchName
}, { }, {
releaseTitle: releaseTitle, releaseTitle: releaseTitle,
releaseComment: comment releaseComment: comment,
}, function (result) { isEmergencyPublish: isEmergencyPublish
}, function (result) {
d.resolve(result); d.resolve(result);
}, function (result) { }, function (result) {
d.reject(result); d.reject(result);
...@@ -98,7 +100,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -98,7 +100,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
}); });
return d.promise; return d.promise;
} }
function findLatestActiveRelease(appId, env, clusterName, namespaceName) { function findLatestActiveRelease(appId, env, clusterName, namespaceName) {
var d = $q.defer(); var d = $q.defer();
resource.find_active_releases({ resource.find_active_releases({
...@@ -109,7 +111,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -109,7 +111,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
page: 0, page: 0,
size: 1 size: 1
}, function (result) { }, function (result) {
if (result && result.length){ if (result && result.length) {
d.resolve(result[0]); d.resolve(result[0]);
} }
......
...@@ -2,7 +2,7 @@ appService.service('ServerConfigService', ['$resource', '$q', function ($resourc ...@@ -2,7 +2,7 @@ appService.service('ServerConfigService', ['$resource', '$q', function ($resourc
var server_config_resource = $resource('', {}, { var server_config_resource = $resource('', {}, {
create_server_config: { create_server_config: {
method: 'POST', method: 'POST',
url:'/server/config' url: '/server/config'
} }
}); });
return { return {
......
...@@ -607,6 +607,12 @@ table th { ...@@ -607,6 +607,12 @@ table th {
padding: 0; padding: 0;
} }
.release-history .release-history-list .emergency-publish {
position: absolute;
left: 0;
top: 0;
}
.release-history .release-history-list .load-more { .release-history .release-history-list .load-more {
height: 45px; height: 45px;
background: #f5f5f5; background: #f5f5f5;
......
<div class="modal fade" id="publishDenyModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header panel-primary">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">发布受限</h4>
</div>
<div class="modal-body">
您不能发布哟~{{env}}环境配置的编辑和发布必须为不同的人,请找另一个具有当前namespace发布权的人操作发布~
<span ng-if="toReleaseNamespace.isEmergencyPublishAllowed">
<br><br><small>(如果是非工作时间或者特殊情况,您可以通过点击<mark>紧急发布</mark>按钮进行发布)</small>
</span>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal"
ng-if="toReleaseNamespace.isEmergencyPublishAllowed"
ng-click="emergencyPublish()">
紧急发布
</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">
关闭
</button>
</div>
</div>
</div>
</div>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册