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

Merge pull request #452 from lepdou/abtest

abtest
......@@ -25,16 +25,24 @@ public class ClusterController {
private ClusterService clusterService;
@RequestMapping(path = "/apps/{appId}/clusters", method = RequestMethod.POST)
public ClusterDTO create(@PathVariable("appId") String appId, @RequestBody ClusterDTO dto) {
public ClusterDTO create(@PathVariable("appId") String appId,
@RequestParam(value = "autoCreatePrivateNamespace", defaultValue = "true") boolean autoCreatePrivateNamespace,
@RequestBody ClusterDTO dto) {
if (!InputValidator.isValidClusterNamespace(dto.getName())) {
throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
Cluster entity = BeanUtils.transfrom(Cluster.class, dto);
Cluster managedEntity = clusterService.findOne(appId, entity.getName());
if (managedEntity != null) {
throw new BadRequestException("cluster already exist.");
}
entity = clusterService.save(entity);
if (autoCreatePrivateNamespace) {
entity = clusterService.saveWithCreatePrivateNamespace(entity);
} else {
entity = clusterService.saveWithoutCreatePrivateNamespace(entity);
}
dto = BeanUtils.transfrom(ClusterDTO.class, entity);
return dto;
......@@ -52,7 +60,7 @@ public class ClusterController {
@RequestMapping("/apps/{appId}/clusters")
public List<ClusterDTO> find(@PathVariable("appId") String appId) {
List<Cluster> clusters = clusterService.findClusters(appId);
List<Cluster> clusters = clusterService.findParentClusters(appId);
return BeanUtils.batchTransform(ClusterDTO.class, clusters);
}
......
package com.ctrip.framework.apollo.adminservice.controller;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
......@@ -159,12 +160,20 @@ public class InstanceConfigController {
}
@RequestMapping(value = "/by-namespace", method = RequestMethod.GET)
public PageDTO<InstanceDTO> getInstancesByNamespace(@RequestParam("appId") String appId,
@RequestParam("clusterName") String clusterName,
@RequestParam("namespaceName") String
namespaceName, Pageable pageable) {
Page<Instance> instances = instanceService.findInstancesByNamespace(appId, clusterName,
namespaceName, pageable);
public PageDTO<InstanceDTO> getInstancesByNamespace(
@RequestParam("appId") String appId, @RequestParam("clusterName") String clusterName,
@RequestParam("namespaceName") String namespaceName,
@RequestParam(value = "instanceAppId", required = false) String instanceAppId,
Pageable pageable) {
Page<Instance> instances;
if (Strings.isNullOrEmpty(instanceAppId)) {
instances = instanceService.findInstancesByNamespace(appId, clusterName,
namespaceName, pageable);
} else {
instances = instanceService.findInstancesByNamespaceAndInstanceAppId(instanceAppId, appId,
clusterName, namespaceName, pageable);
}
List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances.getContent());
return new PageDTO<>(instanceDTOs, pageable, instances.getTotalElements());
}
......
......@@ -45,13 +45,6 @@ public class ItemController {
if (managedEntity != null) {
throw new BadRequestException("item already exist");
} else {
Item lastItem = itemService.findLastOne(appId, clusterName, namespaceName);
int lineNum = 1;
if (lastItem != null) {
Integer lastItemNum = lastItem.getLineNum();
lineNum = lastItemNum == null ? 1 : lastItemNum + 1;
}
entity.setLineNum(lineNum);
entity = itemService.save(entity);
builder.createItem(entity);
}
......@@ -157,4 +150,6 @@ public class ItemController {
}
return BeanUtils.transfrom(ItemDTO.class, item);
}
}
package com.ctrip.framework.apollo.adminservice.controller;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.service.NamespaceBranchService;
import com.ctrip.framework.apollo.biz.utils.ReleaseMessageKeyGenerator;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class NamespaceBranchController {
@Autowired
private MessageSender messageSender;
@Autowired
private NamespaceBranchService namespaceBranchService;
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches", method = RequestMethod.POST)
public NamespaceDTO createBranch(@PathVariable String appId,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam("operator") String operator) {
Namespace createdBranch = namespaceBranchService.createBranch(appId, clusterName, namespaceName, operator);
return BeanUtils.transfrom(NamespaceDTO.class, createdBranch);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules")
public GrayReleaseRuleDTO findBranchGrayRules(@PathVariable String appId,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@PathVariable String branchName) {
GrayReleaseRule rules = namespaceBranchService.findBranchGrayRules(appId, clusterName, namespaceName, branchName);
if (rules == null) {
return null;
}
GrayReleaseRuleDTO ruleDTO =
new GrayReleaseRuleDTO(rules.getAppId(), rules.getClusterName(), rules.getNamespaceName(),
rules.getBranchName());
ruleDTO.setReleaseId(rules.getReleaseId());
ruleDTO.setRuleItems(GrayReleaseRuleItemTransformer.batchTransformFromJSON(rules.getRules()));
return ruleDTO;
}
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules", method = RequestMethod.PUT)
public void updateBranchGrayRules(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String branchName,
@RequestBody GrayReleaseRuleDTO newRuleDto) {
GrayReleaseRule newRules = BeanUtils.transfrom(GrayReleaseRule.class, newRuleDto);
newRules.setRules(GrayReleaseRuleItemTransformer.batchTransformToJSON(newRuleDto.getRuleItems()));
newRules.setBranchStatus(NamespaceBranchStatus.ACTIVE);
namespaceBranchService.updateBranchGrayRules(appId, clusterName, namespaceName, branchName, newRules);
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
}
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}", method = RequestMethod.DELETE)
public void deleteBranch(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String branchName,
@RequestParam("operator") String operator) {
namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName, NamespaceBranchStatus.DELETED, operator);
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches")
public NamespaceDTO loadNamespaceBranch(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName) {
Namespace childNamespace = namespaceBranchService.findBranch(appId, clusterName, namespaceName);
if (childNamespace == null) {
return null;
}
return BeanUtils.transfrom(NamespaceDTO.class, childNamespace);
}
}
package com.ctrip.framework.apollo.adminservice.controller;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.service.NamespaceBranchService;
import com.ctrip.framework.apollo.biz.service.NamespaceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.biz.utils.ReleaseMessageKeyGenerator;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
public class ReleaseController {
private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();
@Autowired
private ReleaseService releaseService;
@Autowired
private NamespaceService namespaceService;
@Autowired
private MessageSender messageSender;
@Autowired
private NamespaceBranchService namespaceBranchService;
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
@RequestMapping("/releases/{releaseId}")
public ReleaseDTO get(@PathVariable("releaseId") long releaseId) {
......@@ -46,6 +56,16 @@ public class ReleaseController {
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
@RequestMapping("/releases")
public List<ReleaseDTO> findReleaseByIds(@RequestParam("releaseIds") String releaseIds){
Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong)
.collect(Collectors.toSet());
List<Release> releases = releaseService.findByReleaseIds(releaseIdSet);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseDTO> findAllReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
......@@ -74,21 +94,66 @@ public class ReleaseController {
}
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
public ReleaseDTO buildRelease(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
@RequestParam("name") String name,
@RequestParam(name = "comment", required = false) String comment,
@RequestParam("operator") String operator) {
public ReleaseDTO publish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
@RequestParam("name") String releaseName,
@RequestParam(name = "comment", required = false) String releaseComment,
@RequestParam("operator") String operator) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName));
}
Release release = releaseService.publish(namespace, releaseName, releaseComment, operator);
//send release message
Namespace parentNamespace = namespaceService.findParentNamespace(namespace);
String messageCluster;
if (parentNamespace != null) {
messageCluster = parentNamespace.getClusterName();
} else {
messageCluster = clusterName;
}
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, messageCluster, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
/**
*merge branch items to master and publish master
* @return published result
*/
@Transactional
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/updateAndPublish", method = RequestMethod.POST)
public ReleaseDTO updateAndPublish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
@RequestParam("releaseName") String releaseName,
@RequestParam("branchName") String branchName,
@RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,
@RequestParam(name = "releaseComment", required = false) String releaseComment,
@RequestBody ItemChangeSets changeSets) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName));
}
Release release = releaseService.buildRelease(name, comment, namespace, operator);
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
Release release = releaseService.mergeBranchChangeSetsAndRelease(namespace, branchName,
releaseName, releaseComment, changeSets);
if (deleteBranch) {
namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName,
NamespaceBranchStatus.MERGED, changeSets.getDataChangeLastModifiedBy());
}
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
@RequestMapping(path = "/releases/{releaseId}/rollback", method = RequestMethod.PUT)
......@@ -101,11 +166,8 @@ public class ReleaseController {
String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName();
//send release message
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
}
private String assembleKey(String appId, String cluster, String namespace) {
return STRING_JOINER.join(appId, cluster, namespace);
}
}
package com.ctrip.framework.apollo.adminservice.controller;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.biz.repository.ReleaseHistoryRepository;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.biz.service.ReleaseHistoryService;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseHistoryDTO;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RestController
public class ReleaseHistoryController {
private Gson gson = new Gson();
private Type configurationTypeReference = new TypeToken<Map<String, Object>>() {
}.getType();
@Autowired
private ReleaseHistoryService releaseHistoryService;
@Autowired
private ReleaseRepository releaseRepository;
@Autowired
private NamespaceRepository namespaceRepository;
@Autowired
private ReleaseHistoryRepository releaseHistoryRepository;
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/histories",
method = RequestMethod.GET)
public PageDTO<ReleaseHistoryDTO> findReleaseHistoriesByNamespace(
@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespaceName, Pageable pageable) {
Page<ReleaseHistory> result = releaseHistoryService.findReleaseHistoriesByNamespace(appId,
clusterName, namespaceName, pageable);
if (!result.hasContent()) {
return null;
}
List<ReleaseHistory> releaseHistories = result.getContent();
List<ReleaseHistoryDTO> releaseHistoryDTOs = new ArrayList<>(releaseHistories.size());
for (ReleaseHistory releaseHistory : releaseHistories) {
ReleaseHistoryDTO dto = new ReleaseHistoryDTO();
BeanUtils.copyProperties(releaseHistory, dto, "operationContext");
dto.setOperationContext(gson.fromJson(releaseHistory.getOperationContext(),
configurationTypeReference));
releaseHistoryDTOs.add(dto);
}
return new PageDTO<>(releaseHistoryDTOs, pageable, result.getTotalElements());
}
@RequestMapping(value = "/release-histories/conversions", method = RequestMethod.POST)
public void releaseHistoryConversion(
@RequestParam(name = "namespaceId", required = false) String namespaceId) {
Iterable<Namespace> namespaces;
if (Strings.isNullOrEmpty(namespaceId)) {
namespaces = namespaceRepository.findAll();
} else {
Set<Long> idList = Arrays.stream(namespaceId.split(",")).map(Long::valueOf).collect
(Collectors.toSet());
namespaces = namespaceRepository.findAll(idList);
}
for (Namespace namespace : namespaces) {
List<Release> releases = releaseRepository
.findByAppIdAndClusterNameAndNamespaceNameOrderByIdAsc(namespace.getAppId(), namespace
.getClusterName(), namespace.getNamespaceName());
if (CollectionUtils.isEmpty(releases)) {
continue;
}
Release previousRelease = null;
Set<ReleaseHistory> releaseHistories = Sets.newLinkedHashSet();//ordered set
for (Release release : releases) {
List<ReleaseHistory> histories = releaseHistoryService.findReleaseHistoriesByReleaseId
(release.getId());
//already processed
if (!CollectionUtils.isEmpty(histories)) {
continue;
}
long previousReleaseId = previousRelease == null ? 0 : previousRelease.getId();
ReleaseHistory releaseHistory = assembleReleaseHistory(
release, ReleaseOperation .NORMAL_RELEASE, previousReleaseId);
releaseHistories.add(releaseHistory);
//rollback
if (release.isAbandoned()) {
releaseHistory.setDataChangeLastModifiedTime(release.getDataChangeCreatedTime());
ReleaseHistory rollBackReleaseHistory = assembleReleaseHistory(previousRelease,
ReleaseOperation.ROLLBACK, release.getId());
rollBackReleaseHistory.setDataChangeCreatedBy(release.getDataChangeLastModifiedBy());
rollBackReleaseHistory.setDataChangeCreatedTime(release.getDataChangeLastModifiedTime());
rollBackReleaseHistory.setDataChangeLastModifiedTime(release.getDataChangeLastModifiedTime());
releaseHistories.add(rollBackReleaseHistory);
} else {
previousRelease = release;
}
}
releaseHistoryRepository.save(releaseHistories);
}
}
public ReleaseHistory assembleReleaseHistory(Release release, int releaseOperation, long
previousReleaseId) {
ReleaseHistory releaseHistory = new ReleaseHistory();
releaseHistory.setAppId(release.getAppId());
releaseHistory.setClusterName(release.getClusterName());
releaseHistory.setNamespaceName(release.getNamespaceName());
releaseHistory.setBranchName(release.getClusterName());
releaseHistory.setReleaseId(release.getId());
releaseHistory.setPreviousReleaseId(previousReleaseId);
releaseHistory.setOperation(releaseOperation);
releaseHistory.setOperationContext("{}"); //default empty object
releaseHistory.setDataChangeCreatedBy(release.getDataChangeCreatedBy());
releaseHistory.setDataChangeCreatedTime(release.getDataChangeCreatedTime());
releaseHistory.setDataChangeLastModifiedTime(release.getDataChangeLastModifiedTime());
releaseHistory.setDataChangeLastModifiedBy("apollo"); //mark
return releaseHistory;
}
}
......@@ -13,6 +13,8 @@ endpoints:
health:
sensitive: false
management:
security:
enabled: false
......
......@@ -243,7 +243,7 @@ public class InstanceConfigControllerTest {
pageable)).thenReturn(instances);
PageDTO<InstanceDTO> result = instanceConfigController.getInstancesByNamespace(someAppId,
someClusterName, someNamespaceName, pageable);
someClusterName, someNamespaceName, null, pageable);
assertEquals(2, result.getContent().size());
InstanceDTO someInstanceDto = null;
......@@ -261,6 +261,47 @@ public class InstanceConfigControllerTest {
verifyInstance(anotherInstance, anotherInstanceDto);
}
@Test
public void testGetInstancesByNamespaceAndInstanceAppId() throws Exception {
String someInstanceAppId = "someInstanceAppId";
String someAppId = "someAppId";
String someClusterName = "someClusterName";
String someNamespaceName = "someNamespaceName";
String someIp = "someIp";
long someInstanceId = 1;
long anotherInstanceId = 2;
Pageable pageable = mock(Pageable.class);
Instance someInstance = assembleInstance(someInstanceId, someAppId, someClusterName,
someNamespaceName, someIp);
Instance anotherInstance = assembleInstance(anotherInstanceId, someAppId, someClusterName,
someNamespaceName, someIp);
Page<Instance> instances = new PageImpl<>(Lists.newArrayList(someInstance, anotherInstance),
pageable, 2);
when(instanceService.findInstancesByNamespaceAndInstanceAppId(someInstanceAppId, someAppId,
someClusterName, someNamespaceName, pageable)).thenReturn(instances);
PageDTO<InstanceDTO> result = instanceConfigController.getInstancesByNamespace(someAppId,
someClusterName, someNamespaceName, someInstanceAppId, pageable);
assertEquals(2, result.getContent().size());
InstanceDTO someInstanceDto = null;
InstanceDTO anotherInstanceDto = null;
for (InstanceDTO instanceDTO : result.getContent()) {
if (instanceDTO.getId() == someInstanceId) {
someInstanceDto = instanceDTO;
} else if (instanceDTO.getId() == anotherInstanceId) {
anotherInstanceDto = instanceDTO;
}
}
verifyInstance(someInstance, someInstanceDto);
verifyInstance(anotherInstance, anotherInstanceDto);
}
@Test
public void testGetInstancesCountByNamespace() throws Exception {
String someAppId = "someAppId";
......
......@@ -121,7 +121,7 @@ public class ReleaseControllerTest extends AbstractControllerTest {
.thenReturn(someNamespace);
releaseController
.buildRelease(someAppId, someCluster, someNamespaceName, someName, someComment, "test");
.publish(someAppId, someCluster, someNamespaceName, someName, someComment, "test");
verify(someMessageSender, times(1))
.sendMessage(Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
......
......@@ -17,4 +17,4 @@ management:
enabled: false
health:
status:
order: DOWN, OUT_OF_SERVICE, UNKNOWN, UP
\ No newline at end of file
order: DOWN, OUT_OF_SERVICE, UNKNOWN, UP
......@@ -5,4 +5,7 @@ spring:
logging:
level:
org.springframework.cloud: 'DEBUG'
file: /opt/logs/100003171/apollo-assembly.log
\ No newline at end of file
file: /opt/logs/100003171/apollo-assembly.log
......@@ -24,6 +24,9 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> {
@Column(name = "AppId", nullable = false)
private String appId;
@Column(name = "ParentClusterId", nullable = false)
private long parentClusterId;
public String getAppId() {
return appId;
}
......@@ -40,8 +43,17 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> {
this.name = name;
}
public long getParentClusterId() {
return parentClusterId;
}
public void setParentClusterId(long parentClusterId) {
this.parentClusterId = parentClusterId;
}
public String toString() {
return toStringHelper().add("name", name).add("appId", appId).toString();
return toStringHelper().add("name", name).add("appId", appId)
.add("parentClusterId", parentClusterId).toString();
}
@Override
......
package com.ctrip.framework.apollo.biz.entity;
import com.ctrip.framework.apollo.common.entity.BaseEntity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "GrayReleaseRule")
@SQLDelete(sql = "Update GrayReleaseRule set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0")
public class GrayReleaseRule extends BaseEntity{
@Column(name = "appId", nullable = false)
private String appId;
@Column(name = "ClusterName", nullable = false)
private String clusterName;
@Column(name = "NamespaceName", nullable = false)
private String namespaceName;
@Column(name = "BranchName", nullable = false)
private String branchName;
@Column(name = "Rules")
private String rules;
@Column(name = "releaseId", nullable = false)
private Long releaseId;
@Column(name = "BranchStatus", nullable = false)
private int branchStatus;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
public String getRules() {
return rules;
}
public void setRules(String rules) {
this.rules = rules;
}
public Long getReleaseId() {
return releaseId;
}
public void setReleaseId(Long releaseId) {
this.releaseId = releaseId;
}
public int getBranchStatus() {
return branchStatus;
}
public void setBranchStatus(int branchStatus) {
this.branchStatus = branchStatus;
}
}
package com.ctrip.framework.apollo.biz.entity;
import com.ctrip.framework.apollo.common.entity.BaseEntity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Entity
@Table(name = "ReleaseHistory")
@SQLDelete(sql = "Update ReleaseHistory set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0")
public class ReleaseHistory extends BaseEntity {
@Column(name = "AppId", nullable = false)
private String appId;
@Column(name = "ClusterName", nullable = false)
private String clusterName;
@Column(name = "NamespaceName", nullable = false)
private String namespaceName;
@Column(name = "BranchName", nullable = false)
private String branchName;
@Column(name = "ReleaseId")
private long releaseId;
@Column(name = "PreviousReleaseId")
private long previousReleaseId;
@Column(name = "Operation")
private int operation;
@Column(name = "OperationContext", nullable = false)
private String operationContext;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public long getPreviousReleaseId() {
return previousReleaseId;
}
public void setPreviousReleaseId(long previousReleaseId) {
this.previousReleaseId = previousReleaseId;
}
public int getOperation() {
return operation;
}
public void setOperation(int operation) {
this.operation = operation;
}
public String getOperationContext() {
return operationContext;
}
public void setOperationContext(String operationContext) {
this.operationContext = operationContext;
}
public String toString() {
return toStringHelper().add("appId", appId).add("clusterName", clusterName)
.add("namespaceName", namespaceName).add("branchName", branchName)
.add("releaseId", releaseId).add("previousReleaseId", previousReleaseId)
.add("operation", operation).toString();
}
}
package com.ctrip.framework.apollo.biz.grayReleaseRule;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleItemDTO;
import java.util.Set;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class GrayReleaseRuleCache {
private long ruleId;
private String branchName;
private String namespaceName;
private long releaseId;
private long loadVersion;
private int branchStatus;
private Set<GrayReleaseRuleItemDTO> ruleItems;
public GrayReleaseRuleCache(long ruleId, String branchName, String namespaceName, long
releaseId, int branchStatus, long loadVersion, Set<GrayReleaseRuleItemDTO> ruleItems) {
this.ruleId = ruleId;
this.branchName = branchName;
this.namespaceName = namespaceName;
this.releaseId = releaseId;
this.branchStatus = branchStatus;
this.loadVersion = loadVersion;
this.ruleItems = ruleItems;
}
public long getRuleId() {
return ruleId;
}
public Set<GrayReleaseRuleItemDTO> getRuleItems() {
return ruleItems;
}
public String getBranchName() {
return branchName;
}
public int getBranchStatus() {
return branchStatus;
}
public long getReleaseId() {
return releaseId;
}
public long getLoadVersion() {
return loadVersion;
}
public void setLoadVersion(long loadVersion) {
this.loadVersion = loadVersion;
}
public String getNamespaceName() {
return namespaceName;
}
public boolean matches(String clientAppId, String clientIp) {
for (GrayReleaseRuleItemDTO ruleItem : ruleItems) {
if (ruleItem.matches(clientAppId, clientIp)) {
return true;
}
}
return false;
}
}
package com.ctrip.framework.apollo.biz.grayReleaseRule;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.message.ReleaseMessageListener;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.repository.GrayReleaseRuleRepository;
import com.ctrip.framework.apollo.biz.service.ServerConfigService;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleItemDTO;
import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class GrayReleaseRulesHolder implements ReleaseMessageListener, InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(GrayReleaseRulesHolder.class);
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
private static final Splitter STRING_SPLITTER =
Splitter.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).omitEmptyStrings();
private static final int DEFAULT_SCAN_INTERVAL_IN_SECONDS = 60;
@Autowired
private ServerConfigService serverConfigService;
@Autowired
private GrayReleaseRuleRepository grayReleaseRuleRepository;
private int databaseScanInterval;
private ScheduledExecutorService executorService;
//store configAppId+configCluster+configNamespace -> GrayReleaseRuleCache map
private Multimap<String, GrayReleaseRuleCache> grayReleaseRuleCache;
//store clientAppId+clientNamespace+ip -> ruleId map
private Multimap<String, Long> reversedGrayReleaseRuleCache;
//an auto increment version to indicate the age of rules
private AtomicLong loadVersion;
public GrayReleaseRulesHolder() {
loadVersion = new AtomicLong();
grayReleaseRuleCache = Multimaps.synchronizedSetMultimap(HashMultimap.create());
reversedGrayReleaseRuleCache = Multimaps.synchronizedSetMultimap(HashMultimap.create());
executorService = Executors.newScheduledThreadPool(1, ApolloThreadFactory
.create("GrayReleaseRulesHolder", true));
}
@Override
public void afterPropertiesSet() throws Exception {
populateDataBaseInterval();
//force sync load for the first time
periodicScanRules();
executorService.scheduleWithFixedDelay(this::periodicScanRules,
getDatabaseScanIntervalSecond(), getDatabaseScanIntervalSecond(), getDatabaseScanTimeUnit()
);
}
@Override
public void handleMessage(ReleaseMessage message, String channel) {
logger.info("message received - channel: {}, message: {}", channel, message);
String releaseMessage = message.getMessage();
if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(releaseMessage)) {
return;
}
List<String> keys = STRING_SPLITTER.splitToList(releaseMessage);
//message should be appId+cluster+namespace
if (keys.size() != 3) {
logger.error("message format invalid - {}", releaseMessage);
return;
}
String appId = keys.get(0);
String cluster = keys.get(1);
String namespace = keys.get(2);
List<GrayReleaseRule> rules = grayReleaseRuleRepository
.findByAppIdAndClusterNameAndNamespaceName(appId, cluster, namespace);
mergeGrayReleaseRules(rules);
}
private void periodicScanRules() {
Transaction transaction = Cat.newTransaction("Apollo.GrayReleaseRulesScanner",
"scanGrayReleaseRules");
try {
loadVersion.incrementAndGet();
scanGrayReleaseRules();
transaction.setStatus(Message.SUCCESS);
} catch (Throwable ex) {
transaction.setStatus(ex);
logger.error("Scan gray release rule failed", ex);
} finally {
transaction.complete();
}
}
public Long findReleaseIdFromGrayReleaseRule(String clientAppId, String clientIp, String
configAppId, String configCluster, String configNamespaceName) {
String key = assembleGrayReleaseRuleKey(configAppId, configCluster, configNamespaceName);
if (!grayReleaseRuleCache.containsKey(key)) {
return null;
}
//create a new list to avoid ConcurrentModificationException
List<GrayReleaseRuleCache> rules = Lists.newArrayList(grayReleaseRuleCache.get(key));
for (GrayReleaseRuleCache rule : rules) {
//check branch status
if (rule.getBranchStatus() != NamespaceBranchStatus.ACTIVE) {
continue;
}
if (rule.matches(clientAppId, clientIp)) {
return rule.getReleaseId();
}
}
return null;
}
/**
* Check whether there are gray release rules for the clientAppId, clientIp, namespace
* combination. Please note that even there are gray release rules, it doesn't mean it will always
* load gray releases. Because gray release rules actually apply to one more dimension - cluster.
*/
public boolean hasGrayReleaseRule(String clientAppId, String clientIp, String namespaceName) {
return reversedGrayReleaseRuleCache.containsKey(assembleReversedGrayReleaseRuleKey(clientAppId,
namespaceName, clientIp)) || reversedGrayReleaseRuleCache.containsKey
(assembleReversedGrayReleaseRuleKey(clientAppId, namespaceName, GrayReleaseRuleItemDTO
.ALL_IP));
}
private void scanGrayReleaseRules() {
long maxIdScanned = 0;
boolean hasMore = true;
while (hasMore && !Thread.currentThread().isInterrupted()) {
List<GrayReleaseRule> grayReleaseRules = grayReleaseRuleRepository
.findFirst500ByIdGreaterThanOrderByIdAsc(maxIdScanned);
if (CollectionUtils.isEmpty(grayReleaseRules)) {
break;
}
mergeGrayReleaseRules(grayReleaseRules);
int rulesScanned = grayReleaseRules.size();
maxIdScanned = grayReleaseRules.get(rulesScanned - 1).getId();
//batch is 500
hasMore = rulesScanned == 500;
}
}
private void mergeGrayReleaseRules(List<GrayReleaseRule> grayReleaseRules) {
if (CollectionUtils.isEmpty(grayReleaseRules)) {
return;
}
for (GrayReleaseRule grayReleaseRule : grayReleaseRules) {
if (grayReleaseRule.getReleaseId() == null || grayReleaseRule.getReleaseId() == 0) {
//filter rules with no release id, i.e. never released
continue;
}
String key = assembleGrayReleaseRuleKey(grayReleaseRule.getAppId(), grayReleaseRule
.getClusterName(), grayReleaseRule.getNamespaceName());
//create a new list to avoid ConcurrentModificationException
List<GrayReleaseRuleCache> rules = Lists.newArrayList(grayReleaseRuleCache.get(key));
GrayReleaseRuleCache oldRule = null;
for (GrayReleaseRuleCache ruleCache : rules) {
if (ruleCache.getBranchName().equals(grayReleaseRule.getBranchName())) {
oldRule = ruleCache;
break;
}
}
//if old rule is null and new rule's branch status is not active, ignore
if (oldRule == null && grayReleaseRule.getBranchStatus() != NamespaceBranchStatus.ACTIVE) {
continue;
}
//use id comparison to avoid synchronization
if (oldRule == null || grayReleaseRule.getId() > oldRule.getRuleId()) {
addCache(key, transformRuleToRuleCache(grayReleaseRule));
if (oldRule != null) {
removeCache(key, oldRule);
}
} else {
if (oldRule.getBranchStatus() == NamespaceBranchStatus.ACTIVE) {
//update load version
oldRule.setLoadVersion(loadVersion.get());
} else if ((loadVersion.get() - oldRule.getLoadVersion()) > 1) {
//remove outdated inactive branch rule after 2 update cycles
removeCache(key, oldRule);
}
}
}
}
private void addCache(String key, GrayReleaseRuleCache ruleCache) {
if (ruleCache.getBranchStatus() == NamespaceBranchStatus.ACTIVE) {
for (GrayReleaseRuleItemDTO ruleItemDTO : ruleCache.getRuleItems()) {
for (String clientIp : ruleItemDTO.getClientIpList()) {
reversedGrayReleaseRuleCache.put(assembleReversedGrayReleaseRuleKey(ruleItemDTO
.getClientAppId(), ruleCache.getNamespaceName(), clientIp), ruleCache.getRuleId());
}
}
}
grayReleaseRuleCache.put(key, ruleCache);
}
private void removeCache(String key, GrayReleaseRuleCache ruleCache) {
grayReleaseRuleCache.remove(key, ruleCache);
for (GrayReleaseRuleItemDTO ruleItemDTO : ruleCache.getRuleItems()) {
for (String clientIp : ruleItemDTO.getClientIpList()) {
reversedGrayReleaseRuleCache.remove(assembleReversedGrayReleaseRuleKey(ruleItemDTO
.getClientAppId(), ruleCache.getNamespaceName(), clientIp), ruleCache.getRuleId());
}
}
}
private GrayReleaseRuleCache transformRuleToRuleCache(GrayReleaseRule grayReleaseRule) {
Set<GrayReleaseRuleItemDTO> ruleItems;
try {
ruleItems = GrayReleaseRuleItemTransformer.batchTransformFromJSON(grayReleaseRule.getRules());
} catch (Throwable ex) {
ruleItems = Sets.newHashSet();
Cat.logError(ex);
logger.error("parse rule for gray release rule {} failed", grayReleaseRule.getId(), ex);
}
GrayReleaseRuleCache ruleCache = new GrayReleaseRuleCache(grayReleaseRule.getId(),
grayReleaseRule.getBranchName(), grayReleaseRule.getNamespaceName(), grayReleaseRule
.getReleaseId(), grayReleaseRule.getBranchStatus(), loadVersion.get(), ruleItems);
return ruleCache;
}
private void populateDataBaseInterval() {
databaseScanInterval = DEFAULT_SCAN_INTERVAL_IN_SECONDS;
try {
String interval = serverConfigService.getValue("apollo.gray-release-rule-scan.interval");
if (!Objects.isNull(interval)) {
databaseScanInterval = Integer.parseInt(interval);
}
} catch (Throwable ex) {
Cat.logError(ex);
logger.error("Load apollo gray release rule scan interval from server config failed", ex);
}
}
private int getDatabaseScanIntervalSecond() {
return databaseScanInterval;
}
private TimeUnit getDatabaseScanTimeUnit() {
return TimeUnit.SECONDS;
}
private String assembleGrayReleaseRuleKey(String configAppId, String configCluster, String
configNamespaceName) {
return STRING_JOINER.join(configAppId, configCluster, configNamespaceName);
}
private String assembleReversedGrayReleaseRuleKey(String clientAppId, String
clientNamespaceName, String clientIp) {
return STRING_JOINER.join(clientAppId, clientNamespaceName, clientIp);
}
}
......@@ -9,7 +9,11 @@ import java.util.List;
public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> {
List<Cluster> findByAppIdAndParentClusterId(String appId, Long parentClusterId);
List<Cluster> findByAppId(String appId);
Cluster findByAppIdAndName(String appId, String name);
List<Cluster> findByParentClusterId(Long parentClusterId);
}
package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface GrayReleaseRuleRepository extends PagingAndSortingRepository<GrayReleaseRule, Long> {
GrayReleaseRule findTopByAppIdAndClusterNameAndNamespaceNameAndBranchNameOrderByIdDesc(String appId, String clusterName,
String namespaceName, String branchName);
List<GrayReleaseRule> findByAppIdAndClusterNameAndNamespaceName(String appId,
String clusterName, String namespaceName);
List<GrayReleaseRule> findFirst500ByIdGreaterThanOrderByIdAsc(Long id);
}
......@@ -4,7 +4,9 @@ import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import java.util.Date;
import java.util.List;
......@@ -12,8 +14,8 @@ import java.util.Set;
public interface InstanceConfigRepository extends PagingAndSortingRepository<InstanceConfig, Long> {
InstanceConfig findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName(long instanceId, String
configAppId, String configClusterName, String configNamespaceName);
InstanceConfig findByInstanceIdAndConfigAppIdAndConfigNamespaceName(long instanceId, String
configAppId, String configNamespaceName);
Page<InstanceConfig> findByReleaseKeyAndDataChangeLastModifiedTimeAfter(String releaseKey, Date
validDate, Pageable pageable);
......@@ -24,4 +26,18 @@ public interface InstanceConfigRepository extends PagingAndSortingRepository<Ins
List<InstanceConfig> findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfterAndReleaseKeyNotIn(
String appId, String clusterName, String namespaceName, Date validDate, Set<String> releaseKey);
@Query(
value = "select b.Id from `InstanceConfig` a inner join `Instance` b on b.Id =" +
" a.`InstanceId` where a.`ConfigAppId` = :configAppId and a.`ConfigClusterName` = " +
":clusterName and a.`ConfigNamespaceName` = :namespaceName and a.`DataChange_LastTime` " +
"> :validDate and b.`AppId` = :instanceAppId and ?#{#pageable.pageSize} > 0",
countQuery = "select count(1) from `InstanceConfig` a inner join `Instance` b on b.id =" +
" a.`InstanceId` where a.`ConfigAppId` = :configAppId and a.`ConfigClusterName` = " +
":clusterName and a.`ConfigNamespaceName` = :namespaceName and a.`DataChange_LastTime` " +
"> :validDate and b.`AppId` = :instanceAppId",
nativeQuery = true)
Page<Object[]> findInstanceIdsByNamespaceAndInstanceAppId(
@Param("instanceAppId") String instanceAppId, @Param("configAppId") String configAppId,
@Param("clusterName") String clusterName, @Param("namespaceName") String namespaceName,
@Param("validDate") Date validDate, Pageable pageable);
}
......@@ -18,4 +18,5 @@ public interface NamespaceRepository extends PagingAndSortingRepository<Namespac
@Query("update Namespace set isdeleted=1,DataChange_LastModifiedBy = ?3 where appId=?1 and clusterName=?2")
int batchDelete(String appId, String clusterName, String operator);
List<Namespace> findByAppIdAndNamespaceName(String appId, String namespaceName);
}
package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ReleaseHistoryRepository extends PagingAndSortingRepository<ReleaseHistory, Long> {
Page<ReleaseHistory> findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(String appId, String
clusterName, String namespaceName, Pageable pageable);
List<ReleaseHistory> findByReleaseId(long releaseId);
}
......@@ -19,13 +19,20 @@ public interface ReleaseRepository extends PagingAndSortingRepository<Release, L
Release findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(@Param("appId") String appId, @Param("clusterName") String clusterName,
@Param("namespaceName") String namespaceName);
Release findByIdAndIsAbandonedFalse(long id);
List<Release> findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page);
List<Release> findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page);
List<Release> findByReleaseKeyIn(Set<String> releaseKey);
List<Release> findByIdIn(Set<Long> releaseIds);
@Modifying
@Query("update Release set isdeleted=1,DataChange_LastModifiedBy = ?4 where appId=?1 and clusterName=?2 and namespaceName = ?3")
int batchDelete(String appId, String clusterName, String namespaceName, String operator);
// For release history conversion program, need to delete after conversion it done
List<Release> findByAppIdAndClusterNameAndNamespaceNameOrderByIdAsc(String appId, String clusterName, String namespaceName);
}
......@@ -122,7 +122,7 @@ public class AppNamespaceService {
}
private void linkPrivateAppNamespaceInAllCluster(String appId, String namespaceName, String createBy) {
List<Cluster> clusters = clusterService.findClusters(appId);
List<Cluster> clusters = clusterService.findParentClusters(appId);
for (Cluster cluster : clusters) {
Namespace namespace = new Namespace();
namespace.setClusterName(cluster.getName());
......
......@@ -39,12 +39,16 @@ public class ClusterService {
return clusterRepository.findByAppIdAndName(appId, name);
}
public List<Cluster> findClusters(String appId) {
public Cluster findOne(long clusterId){
return clusterRepository.findOne(clusterId);
}
public List<Cluster> findParentClusters(String appId) {
if (Strings.isNullOrEmpty(appId)) {
return Collections.emptyList();
}
List<Cluster> clusters = clusterRepository.findByAppId(appId);
List<Cluster> clusters = clusterRepository.findByAppIdAndParentClusterId(appId, 0L);
if (clusters == null) {
return Collections.emptyList();
}
......@@ -55,15 +59,24 @@ public class ClusterService {
}
@Transactional
public Cluster save(Cluster entity) {
public Cluster saveWithCreatePrivateNamespace(Cluster entity) {
Cluster savedCluster = saveWithoutCreatePrivateNamespace(entity);
namespaceService.createPrivateNamespace(savedCluster.getAppId(), savedCluster.getName(),
savedCluster.getDataChangeCreatedBy());
return savedCluster;
}
@Transactional
public Cluster saveWithoutCreatePrivateNamespace(Cluster entity){
if (!isClusterNameUnique(entity.getAppId(), entity.getName())) {
throw new ServiceException("cluster not unique");
throw new BadRequestException("cluster not unique");
}
entity.setId(0);//protection
Cluster cluster = clusterRepository.save(entity);
namespaceService.createPrivateNamespace(cluster.getAppId(), cluster.getName(), cluster.getDataChangeCreatedBy());
auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT,
cluster.getDataChangeCreatedBy());
......@@ -114,4 +127,14 @@ public class ClusterService {
auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT, createBy);
}
public List<Cluster> findChildClusters(String appId, String parentClusterName){
Cluster parentCluster = findOne(appId, parentClusterName);
if (parentCluster == null){
throw new BadRequestException("parent cluster not exist");
}
return clusterRepository.findByParentClusterId(parentCluster.getId());
}
}
package com.ctrip.framework.apollo.biz.service;
import com.google.common.base.Preconditions;
import com.google.common.collect.Interner;
import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.biz.entity.Instance;
......@@ -16,6 +17,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
......@@ -55,10 +57,10 @@ public class InstanceService {
}
public InstanceConfig findInstanceConfig(long instanceId, String configAppId, String
configClusterName, String configNamespaceName) {
configNamespaceName) {
return instanceConfigRepository
.findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName(
instanceId, configAppId, configClusterName, configNamespaceName);
.findByInstanceIdAndConfigAppIdAndConfigNamespaceName(
instanceId, configAppId, configNamespaceName);
}
public Page<InstanceConfig> findActiveInstanceConfigsByReleaseKey(String releaseKey, Pageable
......@@ -85,6 +87,42 @@ public class InstanceService {
return new PageImpl<>(instances, pageable, instanceConfigs.getTotalElements());
}
public Page<Instance> findInstancesByNamespaceAndInstanceAppId(String instanceAppId, String
appId, String clusterName, String
namespaceName, Pageable
pageable) {
Page<Object[]> instanceIdResult = instanceConfigRepository
.findInstanceIdsByNamespaceAndInstanceAppId(instanceAppId, appId, clusterName,
namespaceName, getValidInstanceConfigDate(), pageable);
List<Instance> instances = Collections.emptyList();
if (instanceIdResult.hasContent()) {
Set<Long> instanceIds = instanceIdResult.getContent().stream().map((Object o) -> {
if (o == null) {
return null;
}
if (o instanceof Integer) {
return ((Integer)o).longValue();
}
if (o instanceof Long) {
return (Long) o;
}
//for h2 test
if (o instanceof BigInteger) {
return ((BigInteger) o).longValue();
}
return null;
}).filter((Long value) -> value != null).collect(Collectors.toSet());
instances = findInstancesByIds(instanceIds);
}
return new PageImpl<>(instances, pageable, instanceIdResult.getTotalElements());
}
public List<InstanceConfig> findInstanceConfigsByNamespaceWithReleaseKeysNotIn(String appId,
String clusterName,
String
......@@ -126,6 +164,7 @@ public class InstanceService {
Preconditions.checkArgument(existedInstanceConfig != null, String.format(
"Instance config %d doesn't exist", instanceConfig.getId()));
existedInstanceConfig.setConfigClusterName(instanceConfig.getConfigClusterName());
existedInstanceConfig.setReleaseKey(instanceConfig.getReleaseKey());
existedInstanceConfig.setReleaseDeliveryTime(instanceConfig.getReleaseDeliveryTime());
existedInstanceConfig.setDataChangeLastModifiedTime(instanceConfig
......
......@@ -4,7 +4,6 @@ import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.repository.ItemRepository;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
......@@ -24,7 +23,7 @@ public class ItemService {
private ItemRepository itemRepository;
@Autowired
private NamespaceRepository namespaceRepository;
private NamespaceService namespaceService;
@Autowired
private AuditService auditService;
......@@ -54,8 +53,7 @@ public class ItemService {
}
public Item findOne(String appId, String clusterName, String namespaceName, String key) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId,
clusterName, namespaceName);
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
......@@ -65,14 +63,16 @@ public class ItemService {
}
public Item findLastOne(String appId, String clusterName, String namespaceName) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId,
clusterName, namespaceName);
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
}
Item item = itemRepository.findFirst1ByNamespaceIdOrderByLineNumDesc(namespace.getId());
return item;
return findLastOne(namespace.getId());
}
public Item findLastOne(long namespaceId) {
return itemRepository.findFirst1ByNamespaceIdOrderByLineNumDesc(namespaceId);
}
public Item findOne(long itemId) {
......@@ -87,27 +87,33 @@ public class ItemService {
}
return items;
}
public List<Item> findItems(String appId, String clusterName, String namespaceName) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, clusterName,
namespaceName);
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace != null) {
return findItems(namespace.getId());
} else {
return Collections.emptyList();
}
}
@Transactional
public Item save(Item entity) {
checkItemKeyLength(entity.getKey());
checkItemValueLength(entity.getValue());
entity.setId(0);//protection
if (entity.getLineNum() == 0) {
Item lastItem = findLastOne(entity.getNamespaceId());
int lineNum = lastItem == null ? 1 : lastItem.getLineNum() + 1;
entity.setLineNum(lineNum);
}
Item item = itemRepository.save(entity);
auditService.audit(Item.class.getSimpleName(), item.getId(), Audit.OP.INSERT,
item.getDataChangeCreatedBy());
item.getDataChangeCreatedBy());
return item;
}
......@@ -120,22 +126,22 @@ public class ItemService {
managedItem = itemRepository.save(managedItem);
auditService.audit(Item.class.getSimpleName(), managedItem.getId(), Audit.OP.UPDATE,
managedItem.getDataChangeLastModifiedBy());
managedItem.getDataChangeLastModifiedBy());
return managedItem;
}
private boolean checkItemValueLength(String value){
private boolean checkItemValueLength(String value) {
int lengthLimit = Integer.valueOf(serverConfigService.getValue("item.value.length.limit", "20000"));
if (!StringUtils.isEmpty(value) && value.length() > lengthLimit){
if (!StringUtils.isEmpty(value) && value.length() > lengthLimit) {
throw new BadRequestException("value too long. length limit:" + lengthLimit);
}
return true;
}
private boolean checkItemKeyLength(String key){
private boolean checkItemKeyLength(String key) {
int lengthLimit = Integer.valueOf(serverConfigService.getValue("item.key.length.limit", "128"));
if (!StringUtils.isEmpty(key) && key.length() > lengthLimit){
if (!StringUtils.isEmpty(key) && key.length() > lengthLimit) {
throw new BadRequestException("key too long. length limit:" + lengthLimit);
}
return true;
......
......@@ -8,6 +8,7 @@ import org.springframework.util.CollectionUtils;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Commit;
import com.ctrip.framework.apollo.biz.entity.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.utils.ConfigChangeContentBuilder;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
......@@ -27,6 +28,10 @@ public class ItemSetService {
@Autowired
private ItemService itemService;
@Transactional
public ItemChangeSets updateSet(Namespace namespace, ItemChangeSets changeSets){
return updateSet(namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName(), changeSets);
}
@Transactional
public ItemChangeSets updateSet(String appId, String clusterName,
......
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.saveWithoutCreatePrivateNamespace(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;
}
}
......@@ -13,11 +13,11 @@ public class NamespaceLockService {
@Autowired
private NamespaceLockRepository namespaceLockRepository;
public NamespaceLock findLock(Long namespaceId){
return namespaceLockRepository.findByNamespaceId(namespaceId);
}
@Transactional
public NamespaceLock tryLock(NamespaceLock lock){
return namespaceLockRepository.save(lock);
......
package com.ctrip.framework.apollo.biz.service;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
......@@ -30,7 +36,78 @@ public class NamespaceService {
private CommitService commitService;
@Autowired
private ReleaseService releaseService;
@Autowired
private ClusterService clusterService;
public Namespace findOne(Long namespaceId) {
return namespaceRepository.findOne(namespaceId);
}
public Namespace findOne(String appId, String clusterName, String namespaceName) {
return namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, clusterName,
namespaceName);
}
public List<Namespace> findNamespaces(String appId, String clusterName) {
List<Namespace> namespaces = namespaceRepository.findByAppIdAndClusterNameOrderByIdAsc(appId, clusterName);
if (namespaces == null) {
return Collections.emptyList();
}
return namespaces;
}
public List<Namespace> findByAppIdAndNamespaceName(String appId, String namespaceName){
return namespaceRepository.findByAppIdAndNamespaceName(appId, namespaceName);
}
public Namespace findChildNamespace(String appId, String parentClusterName, String namespaceName){
List<Namespace> namespaces = findByAppIdAndNamespaceName(appId, namespaceName);
if (CollectionUtils.isEmpty(namespaces) || namespaces.size() == 1){
return null;
}
List<Cluster> childClusters = clusterService.findChildClusters(appId, parentClusterName);
if (CollectionUtils.isEmpty(childClusters)){
return null;
}
Set<String> childClusterNames = childClusters.stream().map(Cluster::getName).collect(Collectors.toSet());
//the child namespace is the intersection of the child clusters and child namespaces
for (Namespace namespace: namespaces){
if (childClusterNames.contains(namespace.getClusterName())){
return namespace;
}
}
return null;
}
public Namespace findChildNamespace(Namespace parentNamespace){
String appId = parentNamespace.getAppId();
String parentClusterName = parentNamespace.getClusterName();
String namespaceName = parentNamespace.getNamespaceName();
return findChildNamespace(appId, parentClusterName, namespaceName);
}
public Namespace findParentNamespace(Namespace namespace){
String appId = namespace.getAppId();
String namespaceName = namespace.getNamespaceName();
Cluster cluster = clusterService.findOne(appId, namespace.getClusterName());
if (cluster != null && cluster.getParentClusterId() > 0){
Cluster parentCluster = clusterService.findOne(cluster.getParentClusterId());
return findOne(appId, parentCluster.getName(), namespaceName);
}
return null;
}
public boolean isChildNamespace(Namespace namespace){
return findParentNamespace(namespace) != null;
}
public boolean isNamespaceUnique(String appId, String cluster, String namespace) {
Objects.requireNonNull(appId, "AppId must not be null");
......@@ -59,7 +136,10 @@ public class NamespaceService {
itemService.batchDelete(namespace.getId(), operator);
commitService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator);
releaseService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator);
if (!isChildNamespace(namespace)){
releaseService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator);
}
namespace.setDeleted(true);
namespace.setDataChangeLastModifiedBy(operator);
......@@ -69,23 +149,6 @@ public class NamespaceService {
return namespaceRepository.save(namespace);
}
public Namespace findOne(Long namespaceId) {
return namespaceRepository.findOne(namespaceId);
}
public Namespace findOne(String appId, String clusterName, String namespaceName) {
return namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, clusterName,
namespaceName);
}
public List<Namespace> findNamespaces(String appId, String clusterName) {
List<Namespace> groups = namespaceRepository.findByAppIdAndClusterNameOrderByIdAsc(appId, clusterName);
if (groups == null) {
return Collections.emptyList();
}
return groups;
}
@Transactional
public Namespace save(Namespace entity) {
if (!isNamespaceUnique(entity.getAppId(), entity.getClusterName(), entity.getNamespaceName())) {
......@@ -131,4 +194,6 @@ public class NamespaceService {
}
}
}
package com.ctrip.framework.apollo.biz.service;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import com.ctrip.framework.apollo.biz.repository.ReleaseHistoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Service
public class ReleaseHistoryService {
@Autowired
private ReleaseHistoryRepository releaseHistoryRepository;
@Autowired
private ReleaseService releaseService;
@Autowired
private AuditService auditService;
private Gson gson = new Gson();
public Page<ReleaseHistory> findReleaseHistoriesByNamespace(String appId, String clusterName,
String namespaceName, Pageable
pageable) {
return releaseHistoryRepository.findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(appId, clusterName,
namespaceName, pageable);
}
public List<ReleaseHistory> findReleaseHistoriesByReleaseId(long releaseId) {
return releaseHistoryRepository.findByReleaseId(releaseId);
}
@Transactional
public ReleaseHistory createReleaseHistory(String appId, String clusterName, String
namespaceName, String branchName, long releaseId, long previousReleaseId, int operation,
Map<String, Object> operationContext, String operator) {
ReleaseHistory releaseHistory = new ReleaseHistory();
releaseHistory.setAppId(appId);
releaseHistory.setClusterName(clusterName);
releaseHistory.setNamespaceName(namespaceName);
releaseHistory.setBranchName(branchName);
releaseHistory.setReleaseId(releaseId);
releaseHistory.setPreviousReleaseId(previousReleaseId);
releaseHistory.setOperation(operation);
if (operationContext == null) {
releaseHistory.setOperationContext("{}"); //default empty object
} else {
releaseHistory.setOperationContext(gson.toJson(operationContext));
}
releaseHistory.setDataChangeCreatedTime(new Date());
releaseHistory.setDataChangeCreatedBy(operator);
releaseHistory.setDataChangeLastModifiedBy(operator);
releaseHistoryRepository.save(releaseHistory);
auditService.audit(ReleaseHistory.class.getSimpleName(), releaseHistory.getId(),
Audit.OP.INSERT, releaseHistory.getDataChangeCreatedBy());
return releaseHistory;
}
}
package com.ctrip.framework.apollo.biz.utils;
import com.google.common.base.Joiner;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.core.utils.ByteUtil;
import com.ctrip.framework.apollo.core.utils.MachineUtil;
import org.apache.commons.lang.time.FastDateFormat;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import com.ctrip.framework.apollo.common.utils.UniqueKeyGenerator;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class ReleaseKeyGenerator {
private static final FastDateFormat TIMESTAMP_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss");
private static final AtomicInteger releaseCounter = new AtomicInteger(new SecureRandom().nextInt());
private static final Joiner KEY_JOINER = Joiner.on("-");
public class ReleaseKeyGenerator extends UniqueKeyGenerator {
/**
* Generate the release key in the format: timestamp+appId+cluster+namespace+hash(ipAsInt+counter)
......@@ -30,29 +17,6 @@ public class ReleaseKeyGenerator {
* @return the unique release key
*/
public static String generateReleaseKey(Namespace namespace) {
String hexIdString =
ByteUtil.toHexString(
toByteArray(Objects.hash(namespace.getAppId(), namespace.getClusterName(),
namespace.getNamespaceName()), MachineUtil.getMachineIdentifier(),
releaseCounter.incrementAndGet()));
return KEY_JOINER.join(TIMESTAMP_FORMAT.format(new Date()), hexIdString);
}
/**
* Concat machine id, counter and key to byte array
* Only retrieve lower 3 bytes of the id and counter and 2 bytes of the keyHashCode
*/
private static byte[] toByteArray(int keyHashCode, int machineIdentifier, int counter) {
byte[] bytes = new byte[8];
bytes[0] = ByteUtil.int1(keyHashCode);
bytes[1] = ByteUtil.int0(keyHashCode);
bytes[2] = ByteUtil.int2(machineIdentifier);
bytes[3] = ByteUtil.int1(machineIdentifier);
bytes[4] = ByteUtil.int0(machineIdentifier);
bytes[5] = ByteUtil.int2(counter);
bytes[6] = ByteUtil.int1(counter);
bytes[7] = ByteUtil.int0(counter);
return bytes;
return generate(namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName());
}
}
package com.ctrip.framework.apollo.biz.utils;
import com.google.common.base.Joiner;
import com.ctrip.framework.apollo.core.ConfigConsts;
public class ReleaseMessageKeyGenerator {
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
public static String generate(String appId, String cluster, String namespace) {
return STRING_JOINER.join(appId, cluster, namespace);
}
}
package com.ctrip.framework.apollo.biz;
import com.ctrip.framework.apollo.biz.eureka.ApolloEurekaClientConfigTest;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolderTest;
import com.ctrip.framework.apollo.biz.message.DatabaseMessageSenderTest;
import com.ctrip.framework.apollo.biz.message.ReleaseMessageScannerTest;
import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepositoryTest;
......@@ -9,7 +10,9 @@ import com.ctrip.framework.apollo.biz.service.AdminServiceTest;
import com.ctrip.framework.apollo.biz.service.AdminServiceTransactionTest;
import com.ctrip.framework.apollo.biz.service.ClusterServiceTest;
import com.ctrip.framework.apollo.biz.service.InstanceServiceTest;
import com.ctrip.framework.apollo.biz.service.NamespaceBranchServiceTest;
import com.ctrip.framework.apollo.biz.service.PrivilegeServiceTest;
import com.ctrip.framework.apollo.biz.service.ReleaseCreationTest;
import com.ctrip.framework.apollo.biz.service.ReleaseServiceTest;
import com.ctrip.framework.apollo.biz.service.ServerConfigServiceTest;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGeneratorTest;
......@@ -32,7 +35,10 @@ import org.junit.runners.Suite.SuiteClasses;
ReleaseMessageScannerTest.class,
ClusterServiceTest.class,
ReleaseKeyGeneratorTest.class,
InstanceServiceTest.class
InstanceServiceTest.class,
GrayReleaseRulesHolderTest.class,
NamespaceBranchServiceTest.class,
ReleaseCreationTest.class
})
public class AllTests {
......
package com.ctrip.framework.apollo.biz.grayReleaseRule;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.repository.GrayReleaseRuleRepository;
import com.ctrip.framework.apollo.biz.service.ServerConfigService;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleItemDTO;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class GrayReleaseRulesHolderTest {
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
private GrayReleaseRulesHolder grayReleaseRulesHolder;
@Mock
private ServerConfigService serverConfigService;
@Mock
private GrayReleaseRuleRepository grayReleaseRuleRepository;
private Gson gson = new Gson();
private AtomicLong idCounter;
@Before
public void setUp() throws Exception {
grayReleaseRulesHolder = spy(new GrayReleaseRulesHolder());
ReflectionTestUtils.setField(grayReleaseRulesHolder, "serverConfigService",
serverConfigService);
ReflectionTestUtils.setField(grayReleaseRulesHolder, "grayReleaseRuleRepository",
grayReleaseRuleRepository);
idCounter = new AtomicLong();
}
@Test
public void testScanGrayReleaseRules() throws Exception {
String someAppId = "someAppId";
String someClusterName = "someClusterName";
String someNamespaceName = "someNamespaceName";
String anotherNamespaceName = "anotherNamespaceName";
Long someReleaseId = 1L;
int activeBranchStatus = NamespaceBranchStatus.ACTIVE;
String someClientAppId = "clientAppId1";
String someClientIp = "1.1.1.1";
String anotherClientAppId = "clientAppId2";
String anotherClientIp = "2.2.2.2";
GrayReleaseRule someRule = assembleGrayReleaseRule(someAppId, someClusterName,
someNamespaceName, Lists.newArrayList(assembleRuleItem(someClientAppId, Sets.newHashSet
(someClientIp))), someReleaseId, activeBranchStatus);
when(grayReleaseRuleRepository.findFirst500ByIdGreaterThanOrderByIdAsc(0L)).thenReturn(Lists
.newArrayList(someRule));
//scan rules
grayReleaseRulesHolder.afterPropertiesSet();
assertEquals(someReleaseId, grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule
(someClientAppId, someClientIp, someAppId, someClusterName, someNamespaceName));
assertNull(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(someClientAppId,
anotherClientIp, someAppId, someClusterName, someNamespaceName));
assertNull(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(anotherClientAppId,
someClientIp, someAppId, someClusterName, someNamespaceName));
assertNull(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(anotherClientAppId,
anotherClientIp, someAppId, someClusterName, someNamespaceName));
assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, anotherClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp,
anotherNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp,
anotherNamespaceName));
GrayReleaseRule anotherRule = assembleGrayReleaseRule(someAppId, someClusterName,
someNamespaceName, Lists.newArrayList(assembleRuleItem(anotherClientAppId, Sets.newHashSet
(anotherClientIp))), someReleaseId, activeBranchStatus);
when(grayReleaseRuleRepository.findByAppIdAndClusterNameAndNamespaceName(someAppId,
someClusterName, someNamespaceName)).thenReturn(Lists.newArrayList(anotherRule));
//send message
grayReleaseRulesHolder.handleMessage(assembleReleaseMessage(someAppId, someClusterName,
someNamespaceName), Topics.APOLLO_RELEASE_TOPIC);
assertNull(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule
(someClientAppId, someClientIp, someAppId, someClusterName, someNamespaceName));
assertEquals(someReleaseId, grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule
(anotherClientAppId, anotherClientIp, someAppId, someClusterName, someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(someClientAppId, someClientIp,
anotherNamespaceName));
assertTrue(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, someClientIp,
someNamespaceName));
assertFalse(grayReleaseRulesHolder.hasGrayReleaseRule(anotherClientAppId, anotherClientIp,
anotherNamespaceName));
}
private GrayReleaseRule assembleGrayReleaseRule(String appId, String clusterName, String
namespaceName, List<GrayReleaseRuleItemDTO> ruleItems, long releaseId, int branchStatus) {
GrayReleaseRule rule = new GrayReleaseRule();
rule.setId(idCounter.incrementAndGet());
rule.setAppId(appId);
rule.setClusterName(clusterName);
rule.setNamespaceName(namespaceName);
rule.setBranchName("someBranch");
rule.setRules(gson.toJson(ruleItems));
rule.setReleaseId(releaseId);
rule.setBranchStatus(branchStatus);
return rule;
}
private GrayReleaseRuleItemDTO assembleRuleItem(String clientAppId, Set<String> clientIpList) {
return new GrayReleaseRuleItemDTO(clientAppId, clientIpList);
}
private ReleaseMessage assembleReleaseMessage(String appId, String clusterName, String
namespaceName) {
String message = STRING_JOINER.join(appId, clusterName, namespaceName);
ReleaseMessage releaseMessage = new ReleaseMessage(message);
return releaseMessage;
}
}
......@@ -28,7 +28,7 @@ public class AdminServiceTest extends AbstractIntegrationTest{
private AppRepository appRepository;
@Autowired
private ClusterService clsuterService;
private ClusterService clusterService;
@Autowired
private NamespaceService namespaceService;
......@@ -49,7 +49,7 @@ public class AdminServiceTest extends AbstractIntegrationTest{
app = adminService.createNewApp(app);
Assert.assertEquals(appId, app.getAppId());
List<Cluster> clusters = clsuterService.findClusters(app.getAppId());
List<Cluster> clusters = clusterService.findParentClusters(app.getAppId());
Assert.assertEquals(1, clusters.size());
Assert.assertEquals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusters.get(0).getName());
......
......@@ -86,7 +86,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
String anotherReleaseKey = "anotherReleaseKey";
InstanceConfig instanceConfig = instanceService.findInstanceConfig(someInstanceId,
someConfigAppId, someConfigClusterName, someConfigNamespaceName);
someConfigAppId, someConfigNamespaceName);
assertNull(instanceConfig);
......@@ -94,7 +94,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
someConfigClusterName, someConfigNamespaceName, someReleaseKey));
instanceConfig = instanceService.findInstanceConfig(someInstanceId, someConfigAppId,
someConfigClusterName, someConfigNamespaceName);
someConfigNamespaceName);
assertNotEquals(0, instanceConfig.getId());
assertEquals(someReleaseKey, instanceConfig.getReleaseKey());
......@@ -104,7 +104,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
instanceService.updateInstanceConfig(instanceConfig);
InstanceConfig updated = instanceService.findInstanceConfig(someInstanceId, someConfigAppId,
someConfigClusterName, someConfigNamespaceName);
someConfigNamespaceName);
assertEquals(instanceConfig.getId(), updated.getId());
assertEquals(anotherReleaseKey, updated.getReleaseKey());
......@@ -170,6 +170,42 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
assertEquals(Lists.newArrayList(someInstance, anotherInstance), result.getContent());
}
@Test
@Rollback
public void testFindInstancesByNamespaceAndInstanceAppId() throws Exception {
String someConfigAppId = "someConfigAppId";
String someConfigClusterName = "someConfigClusterName";
String someConfigNamespaceName = "someConfigNamespaceName";
String someReleaseKey = "someReleaseKey";
Date someValidDate = new Date();
String someAppId = "someAppId";
String anotherAppId = "anotherAppId";
String someClusterName = "someClusterName";
String someDataCenter = "someDataCenter";
String someIp = "someIp";
Instance someInstance = instanceService.createInstance(assembleInstance(someAppId,
someClusterName, someDataCenter, someIp));
Instance anotherInstance = instanceService.createInstance(assembleInstance(anotherAppId,
someClusterName, someDataCenter, someIp));
prepareInstanceConfigForInstance(someInstance.getId(), someConfigAppId, someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
prepareInstanceConfigForInstance(anotherInstance.getId(), someConfigAppId,
someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
Page<Instance> result = instanceService.findInstancesByNamespaceAndInstanceAppId(someAppId,
someConfigAppId, someConfigClusterName, someConfigNamespaceName, new PageRequest(0, 10));
Page<Instance> anotherResult = instanceService.findInstancesByNamespaceAndInstanceAppId(anotherAppId,
someConfigAppId, someConfigClusterName, someConfigNamespaceName, new PageRequest(0, 10));
assertEquals(Lists.newArrayList(someInstance), result.getContent());
assertEquals(Lists.newArrayList(anotherInstance), anotherResult.getContent());
}
@Test
@Rollback
public void testFindInstanceConfigsByNamespaceWithReleaseKeysNotIn() throws Exception {
......
package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.entity.ReleaseHistory;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.constants.ReleaseOperation;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.util.AssertionErrors;
public class NamespaceBranchServiceTest extends AbstractIntegrationTest {
@Autowired
private NamespaceBranchService namespaceBranchService;
@Autowired
private ReleaseHistoryService releaseHistoryService;
private String testApp = "test";
private String testCluster = "default";
private String testNamespace = "application";
private String testBranchName = "child-cluster";
private String operator = "apollo";
private Pageable pageable = new PageRequest(0, 10);
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testFindBranch() {
Namespace branch = namespaceBranchService.findBranch(testApp, testCluster, testNamespace);
Assert.assertNotNull(branch);
Assert.assertEquals(testBranchName, branch.getClusterName());
}
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateBranchGrayRulesWithUpdateOnce() {
GrayReleaseRule rule = instanceGrayReleaseRule();
namespaceBranchService.updateBranchGrayRules(testApp, testCluster, testNamespace, testBranchName, rule);
GrayReleaseRule
activeRule =
namespaceBranchService.findBranchGrayRules(testApp, testCluster, testNamespace, testBranchName);
Assert.assertNotNull(activeRule);
Assert.assertEquals(rule.getAppId(), activeRule.getAppId());
Assert.assertEquals(rule.getRules(), activeRule.getRules());
Assert.assertEquals(Long.valueOf(0), activeRule.getReleaseId());
Page<ReleaseHistory> releaseHistories = releaseHistoryService.findReleaseHistoriesByNamespace
(testApp, testCluster, testNamespace, pageable);
ReleaseHistory releaseHistory = releaseHistories.getContent().get(0);
Assert.assertEquals(1, releaseHistories.getTotalElements());
Assert.assertEquals(ReleaseOperation.APPLY_GRAY_RULES, releaseHistory.getOperation());
Assert.assertEquals(0, releaseHistory.getReleaseId());
Assert.assertEquals(0, releaseHistory.getPreviousReleaseId());
Assert.assertTrue(releaseHistory.getOperationContext().contains(rule.getRules()));
}
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateBranchGrayRulesWithUpdateTwice() {
GrayReleaseRule firstRule = instanceGrayReleaseRule();
namespaceBranchService.updateBranchGrayRules(testApp, testCluster, testNamespace, testBranchName, firstRule);
GrayReleaseRule secondRule = instanceGrayReleaseRule();
secondRule.setRules("[{\"clientAppId\":\"branch-test\",\"clientIpList\":[\"10.38.57.112\"]}]");
namespaceBranchService.updateBranchGrayRules(testApp, testCluster, testNamespace, testBranchName, secondRule);
GrayReleaseRule
activeRule =
namespaceBranchService.findBranchGrayRules(testApp, testCluster, testNamespace, testBranchName);
Assert.assertNotNull(secondRule);
Assert.assertEquals(secondRule.getAppId(), activeRule.getAppId());
Assert.assertEquals(secondRule.getRules(), activeRule.getRules());
Assert.assertEquals(Long.valueOf(0), activeRule.getReleaseId());
Page<ReleaseHistory> releaseHistories = releaseHistoryService.findReleaseHistoriesByNamespace
(testApp, testCluster, testNamespace, pageable);
ReleaseHistory firstReleaseHistory = releaseHistories.getContent().get(1);
ReleaseHistory secondReleaseHistory = releaseHistories.getContent().get(0);
Assert.assertEquals(2, releaseHistories.getTotalElements());
Assert.assertEquals(ReleaseOperation.APPLY_GRAY_RULES, firstReleaseHistory.getOperation());
Assert.assertEquals(ReleaseOperation.APPLY_GRAY_RULES, secondReleaseHistory.getOperation());
Assert.assertTrue(firstReleaseHistory.getOperationContext().contains(firstRule.getRules()));
Assert.assertFalse(firstReleaseHistory.getOperationContext().contains(secondRule.getRules()));
Assert.assertTrue(secondReleaseHistory.getOperationContext().contains(firstRule.getRules()));
Assert.assertTrue(secondReleaseHistory.getOperationContext().contains(secondRule.getRules()));
}
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateRulesReleaseIdWithOldRuleNotExist() {
long latestReleaseId = 100;
namespaceBranchService
.updateRulesReleaseId(testApp, testCluster, testNamespace, testBranchName, latestReleaseId, operator);
GrayReleaseRule
activeRule =
namespaceBranchService.findBranchGrayRules(testApp, testCluster, testNamespace, testBranchName);
Assert.assertNull(activeRule);
}
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testUpdateRulesReleaseIdWithOldRuleExist() {
GrayReleaseRule rule = instanceGrayReleaseRule();
namespaceBranchService.updateBranchGrayRules(testApp, testCluster, testNamespace, testBranchName, rule);
long latestReleaseId = 100;
namespaceBranchService
.updateRulesReleaseId(testApp, testCluster, testNamespace, testBranchName, latestReleaseId, operator);
GrayReleaseRule
activeRule =
namespaceBranchService.findBranchGrayRules(testApp, testCluster, testNamespace, testBranchName);
Assert.assertNotNull(activeRule);
Assert.assertEquals(Long.valueOf(latestReleaseId), activeRule.getReleaseId());
Assert.assertEquals(rule.getRules(), activeRule.getRules());
Assert.assertEquals(NamespaceBranchStatus.ACTIVE, activeRule.getBranchStatus());
}
@Test
@Sql(scripts = "/sql/namespace-branch-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testDeleteBranch() {
GrayReleaseRule rule = instanceGrayReleaseRule();
namespaceBranchService.updateBranchGrayRules(testApp, testCluster, testNamespace, testBranchName, rule);
namespaceBranchService.deleteBranch(testApp, testCluster, testNamespace, testBranchName, NamespaceBranchStatus.DELETED, operator);
Namespace branch = namespaceBranchService.findBranch(testApp, testCluster, testNamespace);
Assert.assertNull(branch);
GrayReleaseRule latestRule = namespaceBranchService.findBranchGrayRules(testApp, testCluster, testNamespace, testBranchName);
Assert.assertNotNull(latestRule);
Assert.assertEquals(NamespaceBranchStatus.DELETED, latestRule.getBranchStatus());
Assert.assertEquals("[]", latestRule.getRules());
Page<ReleaseHistory> releaseHistories = releaseHistoryService.findReleaseHistoriesByNamespace
(testApp, testCluster, testNamespace, pageable);
ReleaseHistory firstReleaseHistory = releaseHistories.getContent().get(1);
ReleaseHistory secondReleaseHistory = releaseHistories.getContent().get(0);
Assert.assertEquals(2, releaseHistories.getTotalElements());
Assert.assertEquals(ReleaseOperation.APPLY_GRAY_RULES, firstReleaseHistory.getOperation());
Assert.assertEquals(ReleaseOperation.ABANDON_GRAY_RELEASE, secondReleaseHistory.getOperation());
}
private GrayReleaseRule instanceGrayReleaseRule() {
GrayReleaseRule rule = new GrayReleaseRule();
rule.setAppId(testApp);
rule.setClusterName(testCluster);
rule.setNamespaceName(testNamespace);
rule.setBranchName(testBranchName);
rule.setBranchStatus(NamespaceBranchStatus.ACTIVE);
rule.setRules("[{\"clientAppId\":\"test\",\"clientIpList\":[\"1.0.0.4\"]}]");
return rule;
}
}
......@@ -40,7 +40,7 @@ public class PrivilegeServiceTest extends AbstractIntegrationTest {
app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app);
List<Cluster> clusters = clusterService.findClusters(newApp.getAppId());
List<Cluster> clusters = clusterService.findParentClusters(newApp.getAppId());
List<Namespace> namespaces =
namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName());
Namespace namespace = namespaces.get(0);
......@@ -70,7 +70,7 @@ public class PrivilegeServiceTest extends AbstractIntegrationTest {
app.setDataChangeLastModifiedBy(owner);
app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app);
List<Cluster> clusters = clusterService.findClusters(newApp.getAppId());
List<Cluster> clusters = clusterService.findParentClusters(newApp.getAppId());
List<Namespace> namespaces =
namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName());
Namespace namespace = namespaces.get(0);
......
......@@ -32,6 +32,10 @@ public class ReleaseServiceTest extends AbstractUnitTest {
private ReleaseRepository releaseRepository;
@Mock
private NamespaceService namespaceService;
@Mock
private ReleaseHistoryService releaseHistoryService;
@Mock
private ItemSetService itemSetService;
@InjectMocks
private ReleaseService releaseService;
......
INSERT INTO AppNamespace (AppId, Name, IsPublic) VALUES ('100003171', 'application', false);
INSERT INTO AppNamespace (AppId, Name, IsPublic) VALUES ('100003171', 'fx.apollo.config', true);
INSERT INTO AppNamespace (AppId, Name, IsPublic) VALUES ('100003172', 'application', false);
......
DELETE FROM App;
DELETE FROM Cluster;
DELETE FROM namespace;
DELETE FROM grayreleaserule;
DELETE FROM release;
DELETE FROM item;
DELETE FROM releasemessage;
DELETE FROM releasehistory;
INSERT INTO `app` ( `AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES('test', 'test0620-06', 'default', 'default', 'default', 'default', 0, 'default', 'default');
INSERT INTO `cluster` (`ID`, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (1, 'default', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (`Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES('child-cluster', 'test', 1, 0, 'default', 'default');
INSERT INTO `namespace` (`AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES('test', 'default', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (`AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES('test', 'child-cluster', 'application', 0, 'apollo', 'apollo');
INSERT INTO `app` ( `AppId`, `Name`, `OrgId`, `OrgName`, `OwnerName`, `OwnerEmail`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES('test', 'test0620-06', 'default', 'default', 'default', 'default', 0, 'default', 'default');
/* normal namespace*/
INSERT INTO `cluster` ( `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES ( 'only-master', 'test', 0, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(100, 'test', 'only-master', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(100, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(100, 'k2', 'v2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(100, 'k3', 'v3', '', 'apollo', 'apollo');
/* namespace has branch. master has items but branch has not item*/
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (101, 'default1', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(102, 'child-cluster1', 'test', 101, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(101, 'test', 'default1', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(102, 'test', 'child-cluster1', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(101, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(101, 'k2', 'v2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(101, 'k3', 'v3', '', 'apollo', 'apollo');
INSERT INTO `grayreleaserule` (`AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)VALUES ('test', 'default1', 'application', 'child-cluster1', '[]', 1155, 1);
/* namespace has branch. master has items and branch has item*/
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (103, 'default2', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(104, 'child-cluster2', 'test', 103, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(103, 'test', 'default2', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(104, 'test', 'child-cluster2', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(103, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(103, 'k2', 'v2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(103, 'k3', 'v3', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(104, 'k1', 'v1-1', '', 'apollo', 'apollo');
INSERT INTO `grayreleaserule` (`AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)VALUES ('test', 'default2', 'application', 'child-cluster2', '[]', 1155, 1);
/* namespace has branch. master has items and branch has cover item */
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (105, 'default3', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(106, 'child-cluster3', 'test', 105, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(105, 'test', 'default3', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(106, 'test', 'child-cluster3', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(105, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(105, 'k2', 'v2-2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(106, 'k1', 'v1-2', '', 'apollo', 'apollo');
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(1, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'default3', 'application', '{"k1":"v1","k2":"v2","k3":"v3"}', 0);
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(2, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'child-cluster3', 'application', '{"k1":"v1-1","k2":"v2","k3":"v3"}', 0);
INSERT INTO `grayreleaserule` (`AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)VALUES ('test', 'default3', 'application', 'child-cluster3', '[]', 1155, 1);
/*publish branch at first time */
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (107, 'default4', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(108, 'child-cluster4', 'test', 107, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(107, 'test', 'default4', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(108, 'test', 'child-cluster4', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(107, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(107, 'k2', 'v2-2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(108, 'k1', 'v1-2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(108, 'k4', 'v4', '', 'apollo', 'apollo');
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(3, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'default4', 'application', '{"k1":"v1","k2":"v2","k3":"v3"}', 0);
INSERT INTO `grayreleaserule` (`AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)VALUES ('test', 'default4', 'application', 'child-cluster4', '[]', 1155, 1);
/*publish branch*/
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (109, 'default5', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1010, 'child-cluster5', 'test', 109, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(109, 'test', 'default5', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1010, 'test', 'child-cluster5', 'application', 0, 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(109, 'k1', 'v1', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(109, 'k2', 'v2-2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1010, 'k1', 'v1-2', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1010, 'k4', 'v4', '', 'apollo', 'apollo');
INSERT INTO `item` (`NamespaceId`, `Key`, `Value`, `Comment`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1010, 'k6', 'v6', '', 'apollo', 'apollo');
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(4, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'default5', 'application', '{"k1":"v1","k2":"v2","k3":"v3"}', 0);
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(5, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'child-cluster5', 'application', '{"k1":"v1-1","k2":"v2","k3":"v3","k4":"v4","k5":"v5"}', 0);
INSERT INTO `grayreleaserule` (`AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)VALUES ('test', 'default5', 'application', 'child-cluster5', '[]', 1155, 1);
/* rollback */
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`) VALUES (1011, 'default6', 'test', 0, 0, 'default', 'default');
INSERT INTO `cluster` (ID, `Name`, `AppId`, `ParentClusterId`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1012, 'child-cluster6', 'test', 1011, 0, 'default', 'default');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1011, 'test', 'default6', 'application', 0, 'apollo', 'apollo');
INSERT INTO `namespace` (ID, `AppId`, `ClusterName`, `NamespaceName`, `IsDeleted`, `DataChange_CreatedBy`, `DataChange_LastModifiedBy`)VALUES(1012, 'test', 'child-cluster6', 'application', 0, 'apollo', 'apollo');
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(6, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'default6', 'application', '{"k1":"v1-1","k2":"v2-1","k3":"v3"}', 0);
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(7, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'default6', 'application', '{"k1":"v1","k2":"v2"}', 0);
INSERT INTO `release` (`Id`, `ReleaseKey`, `Name`, `Comment`, `AppId`, `ClusterName`, `NamespaceName`, `Configurations`, `IsAbandoned`)VALUES(8, '20160823102253-fc0071ddf9fd3260', '20160823101703-release', '', 'test', 'child-cluster6', 'application', '{"k1":"v1-2","k2":"v2-1","k3":"v3"}', 0);
package com.ctrip.framework.apollo.ds;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.foundation.Foundation;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.unidal.dal.jdbc.datasource.DataSourceProvider;
......@@ -7,11 +12,6 @@ import org.unidal.dal.jdbc.datasource.model.entity.DataSourcesDef;
import org.unidal.dal.jdbc.datasource.model.transform.DefaultSaxParser;
import org.unidal.lookup.annotation.Named;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.foundation.Foundation;
/**
* Data source provider based on Apollo configuration service.
* <p>
......
package com.ctrip.framework.apollo.model;
import com.google.common.base.Objects;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
/**
......
......@@ -47,7 +47,7 @@ public class ConfigUtil {
String appId = Foundation.app().getAppId();
if (Strings.isNullOrEmpty(appId)) {
appId = ConfigConsts.NO_APPID_PLACEHOLDER;
logger.warn("app.id is not set, apollo will only load public namespace configurations!");
logger.warn("app.id is not set, please make sure it is set in classpath:/META-INF/app.properties, now apollo will only load public namespace configurations!");
}
return appId;
}
......
package com.ctrip.framework.apollo.common.constants;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleItemDTO;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
public interface GsonType {
Type CONFIG = new TypeToken<Map<String, String>>() {}.getType();
}
package com.ctrip.framework.apollo.common.constants;
public interface NamespaceBranchStatus {
int DELETED = 0;
int ACTIVE = 1;
int MERGED = 2;
}
package com.ctrip.framework.apollo.common.constants;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ReleaseOperation {
int NORMAL_RELEASE = 0;
int ROLLBACK = 1;
int GRAY_RELEASE = 2;
int APPLY_GRAY_RULES = 3;
int GRAY_RELEASE_MERGE_TO_MASTER = 4;
int MASTER_NORMAL_RELEASE_MERGE_TO_GRAY = 5;
int MATER_ROLLBACK_MERGE_TO_GRAY = 6;
int ABANDON_GRAY_RELEASE = 7;
int GRAY_RELEASE_DELETED_AFTER_MERGE = 8;
}
package com.ctrip.framework.apollo.common.constants;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface ReleaseOperationContext {
String SOURCE_BRANCH = "sourceBranch";
String RULES = "rules";
String OLD_RULES = "oldRules";
String BASE_RELEASE_ID = "baseReleaseId";
}
......@@ -3,12 +3,10 @@ package com.ctrip.framework.apollo.common.controller;
import javax.servlet.DispatcherType;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.dianping.cat.servlet.CatFilter;
import com.dianping.cat.servlet.CatListener;
@Configuration
public class CatConfig {
......@@ -23,11 +21,4 @@ public class CatConfig {
return bean;
}
@Bean
public ServletListenerRegistrationBean<CatListener> catListener() {
ServletListenerRegistrationBean<CatListener> bean =
new ServletListenerRegistrationBean<CatListener>(new CatListener());
bean.setName("cat-listener");
return bean;
}
}
......@@ -8,6 +8,8 @@ public class ClusterDTO extends BaseDTO{
private String appId;
private long parentClusterId;
public long getId() {
return id;
}
......@@ -31,4 +33,12 @@ public class ClusterDTO extends BaseDTO{
public void setAppId(String appId) {
this.appId = appId;
}
public long getParentClusterId() {
return parentClusterId;
}
public void setParentClusterId(long parentClusterId) {
this.parentClusterId = parentClusterId;
}
}
package com.ctrip.framework.apollo.common.dto;
import com.google.common.collect.Sets;
import java.util.Set;
public class GrayReleaseRuleDTO extends BaseDTO {
private String appId;
private String clusterName;
private String namespaceName;
private String branchName;
private Set<GrayReleaseRuleItemDTO> ruleItems;
private Long releaseId;
public GrayReleaseRuleDTO(String appId, String clusterName, String namespaceName, String branchName) {
this.appId = appId;
this.clusterName = clusterName;
this.namespaceName = namespaceName;
this.branchName = branchName;
this.ruleItems = Sets.newHashSet();
}
public String getAppId() {
return appId;
}
public String getClusterName() {
return clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public String getBranchName() {
return branchName;
}
public Set<GrayReleaseRuleItemDTO> getRuleItems() {
return ruleItems;
}
public void setRuleItems(Set<GrayReleaseRuleItemDTO> ruleItems) {
this.ruleItems = ruleItems;
}
public void addRuleItem(GrayReleaseRuleItemDTO ruleItem) {
this.ruleItems.add(ruleItem);
}
public Long getReleaseId() {
return releaseId;
}
public void setReleaseId(Long releaseId) {
this.releaseId = releaseId;
}
}
package com.ctrip.framework.apollo.common.dto;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class GrayReleaseRuleItemDTO {
public static final String ALL_IP = "*";
private String clientAppId;
private Set<String> clientIpList;
public GrayReleaseRuleItemDTO(String clientAppId) {
this(clientAppId, Sets.newHashSet());
}
public GrayReleaseRuleItemDTO(String clientAppId, Set<String> clientIpList) {
this.clientAppId = clientAppId;
this.clientIpList = clientIpList;
}
public String getClientAppId() {
return clientAppId;
}
public Set<String> getClientIpList() {
return clientIpList;
}
public boolean matches(String clientAppId, String clientIp) {
return appIdMatches(clientAppId) && ipMatches(clientIp);
}
private boolean appIdMatches(String clientAppId) {
return this.clientAppId.equals(clientAppId);
}
private boolean ipMatches(String clientIp) {
return this.clientIpList.contains(ALL_IP) || clientIpList.contains(clientIp);
}
@Override
public String toString() {
return toStringHelper(this).add("clientAppId", clientAppId)
.add("clientIpList", clientIpList).toString();
}
}
......@@ -37,4 +37,8 @@ public class PageDTO<T> {
public int getSize() {
return size;
}
public boolean hasContent(){
return content != null && content.size() > 0;
}
}
package com.ctrip.framework.apollo.common.dto;
import java.util.Map;
public class ReleaseHistoryDTO extends BaseDTO{
private long id;
private String appId;
private String clusterName;
private String namespaceName;
private String branchName;
private long releaseId;
private long previousReleaseId;
private int operation;
private Map<String, Object> operationContext;
public ReleaseHistoryDTO(){}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getClusterName() {
return clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
public long getReleaseId() {
return releaseId;
}
public void setReleaseId(long releaseId) {
this.releaseId = releaseId;
}
public long getPreviousReleaseId() {
return previousReleaseId;
}
public void setPreviousReleaseId(long previousReleaseId) {
this.previousReleaseId = previousReleaseId;
}
public int getOperation() {
return operation;
}
public void setOperation(int operation) {
this.operation = operation;
}
public Map<String, Object> getOperationContext() {
return operationContext;
}
public void setOperationContext(Map<String, Object> operationContext) {
this.operationContext = operationContext;
}
}
package com.ctrip.framework.apollo.common.utils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleItemDTO;
import java.lang.reflect.Type;
import java.util.Set;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class GrayReleaseRuleItemTransformer {
private static final Gson gson = new Gson();
private static final Type grayReleaseRuleItemsType = new TypeToken<Set<GrayReleaseRuleItemDTO>>() {
}.getType();
public static Set<GrayReleaseRuleItemDTO> batchTransformFromJSON(String content) {
return gson.fromJson(content, grayReleaseRuleItemsType);
}
public static String batchTransformToJSON(Set<GrayReleaseRuleItemDTO> ruleItems) {
return gson.toJson(ruleItems);
}
}
package com.ctrip.framework.apollo.common.utils;
import com.google.common.base.Joiner;
import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.core.utils.ByteUtil;
import com.ctrip.framework.apollo.core.utils.MachineUtil;
import org.apache.commons.lang.time.FastDateFormat;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public class UniqueKeyGenerator {
private static final FastDateFormat TIMESTAMP_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss");
private static final AtomicInteger counter = new AtomicInteger(new SecureRandom().nextInt());
private static final Joiner KEY_JOINER = Joiner.on("-");
public static String generate(Object... args){
String hexIdString =
ByteUtil.toHexString(toByteArray(Objects.hash(args), MachineUtil.getMachineIdentifier(),
counter.incrementAndGet()));
return KEY_JOINER.join(TIMESTAMP_FORMAT.format(new Date()), hexIdString);
}
/**
* Concat machine id, counter and key to byte array
* Only retrieve lower 3 bytes of the id and counter and 2 bytes of the keyHashCode
*/
protected static byte[] toByteArray(int keyHashCode, int machineIdentifier, int counter) {
byte[] bytes = new byte[8];
bytes[0] = ByteUtil.int1(keyHashCode);
bytes[1] = ByteUtil.int0(keyHashCode);
bytes[2] = ByteUtil.int2(machineIdentifier);
bytes[3] = ByteUtil.int1(machineIdentifier);
bytes[4] = ByteUtil.int0(machineIdentifier);
bytes[5] = ByteUtil.int2(counter);
bytes[6] = ByteUtil.int1(counter);
bytes[7] = ByteUtil.int0(counter);
return bytes;
}
}
package com.ctrip.framework.apollo.configservice;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolder;
import com.ctrip.framework.apollo.biz.message.ReleaseMessageScanner;
import com.ctrip.framework.apollo.configservice.controller.ConfigFileController;
import com.ctrip.framework.apollo.configservice.controller.NotificationController;
......@@ -14,21 +15,34 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
public class ConfigServiceAutoConfiguration {
@Autowired
private NotificationController notificationController;
@Autowired
private ConfigFileController configFileController;
@Autowired
private NotificationControllerV2 notificationControllerV2;
@Bean
public ReleaseMessageScanner releaseMessageScanner() {
ReleaseMessageScanner releaseMessageScanner = new ReleaseMessageScanner();
//handle server cache first
releaseMessageScanner.addMessageListener(configFileController);
releaseMessageScanner.addMessageListener(notificationControllerV2);
releaseMessageScanner.addMessageListener(notificationController);
return releaseMessageScanner;
public GrayReleaseRulesHolder grayReleaseRulesHolder() {
return new GrayReleaseRulesHolder();
}
@Configuration
static class MessageScannerConfiguration {
@Autowired
private NotificationController notificationController;
@Autowired
private ConfigFileController configFileController;
@Autowired
private NotificationControllerV2 notificationControllerV2;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
@Bean
public ReleaseMessageScanner releaseMessageScanner() {
ReleaseMessageScanner releaseMessageScanner = new ReleaseMessageScanner();
//1. handle gray release rule
releaseMessageScanner.addMessageListener(grayReleaseRulesHolder);
//2. handle server cache
releaseMessageScanner.addMessageListener(configFileController);
//3. notify clients
releaseMessageScanner.addMessageListener(notificationControllerV2);
releaseMessageScanner.addMessageListener(notificationController);
return releaseMessageScanner;
}
}
}
......@@ -10,6 +10,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolder;
import com.ctrip.framework.apollo.biz.service.AppNamespaceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
......@@ -51,6 +52,8 @@ public class ConfigController {
private NamespaceUtil namespaceUtil;
@Autowired
private InstanceConfigAuditUtil instanceConfigAuditUtil;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
private static final Gson gson = new Gson();
private static final Type configurationTypeReference =
......@@ -80,7 +83,8 @@ public class ConfigController {
String appClusterNameLoaded = clusterName;
if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
Release currentAppRelease = loadConfig(appId, clusterName, namespace, dataCenter);
Release currentAppRelease = loadConfig(appId, clientIp, appId, clusterName, namespace,
dataCenter);
if (currentAppRelease != null) {
releases.add(currentAppRelease);
......@@ -91,7 +95,8 @@ public class ConfigController {
//if namespace does not belong to this appId, should check if there is a public configuration
if (!namespaceBelongsToAppId(appId, namespace)) {
Release publicRelease = this.findPublicConfig(appId, clusterName, namespace, dataCenter);
Release publicRelease = this.findPublicConfig(appId, clientIp, clusterName, namespace,
dataCenter);
if (!Objects.isNull(publicRelease)) {
releases.add(publicRelease);
}
......@@ -146,30 +151,31 @@ public class ConfigController {
}
/**
* @param applicationId the application which uses public config
* @param namespace the namespace
* @param dataCenter the datacenter
* @param clientAppId the application which uses public config
* @param namespace the namespace
* @param dataCenter the datacenter
*/
private Release findPublicConfig(String applicationId, String clusterName, String namespace,
private Release findPublicConfig(String clientAppId, String clientIp, String clusterName,
String namespace,
String dataCenter) {
AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespace);
//check whether the namespace's appId equals to current one
if (Objects.isNull(appNamespace) || Objects.equals(applicationId, appNamespace.getAppId())) {
if (Objects.isNull(appNamespace) || Objects.equals(clientAppId, appNamespace.getAppId())) {
return null;
}
String publicConfigAppId = appNamespace.getAppId();
return loadConfig(publicConfigAppId, clusterName, namespace, dataCenter);
return loadConfig(clientAppId, clientIp, publicConfigAppId, clusterName, namespace, dataCenter);
}
private Release loadConfig(String appId, String clusterName, String namespace, String
dataCenter) {
private Release loadConfig(String clientAppId, String clientIp, String configAppId, String
configClusterName, String configNamespace, String dataCenter) {
//load from specified cluster fist
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusterName)) {
Release clusterRelease =
releaseService.findLatestActiveRelease(appId, clusterName, namespace);
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, configClusterName)) {
Release clusterRelease = findRelease(clientAppId, clientIp, configAppId, configClusterName,
configNamespace);
if (!Objects.isNull(clusterRelease)) {
return clusterRelease;
......@@ -177,17 +183,36 @@ public class ConfigController {
}
//try to load via data center
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, clusterName)) {
Release dataCenterRelease =
releaseService.findLatestActiveRelease(appId, dataCenter, namespace);
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, configClusterName)) {
Release dataCenterRelease = findRelease(clientAppId, clientIp, configAppId, dataCenter,
configNamespace);
if (!Objects.isNull(dataCenterRelease)) {
return dataCenterRelease;
}
}
//fallback to default release
return releaseService
.findLatestActiveRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
return findRelease(clientAppId, clientIp, configAppId, ConfigConsts.CLUSTER_NAME_DEFAULT,
configNamespace);
}
private Release findRelease(String clientAppId, String clientIp, String configAppId, String
configClusterName, String configNamespace) {
Long grayReleaseId = grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(clientAppId,
clientIp, configAppId, configClusterName, configNamespace);
Release release = null;
if (grayReleaseId != null) {
release = releaseService.findActiveOne(grayReleaseId);
}
if (release == null) {
release = releaseService.findLatestActiveRelease(configAppId, configClusterName,
configNamespace);
}
return release;
}
/**
......@@ -217,7 +242,8 @@ public class ConfigController {
return;
}
for (Release release : releases) {
instanceConfigAuditUtil.audit(appId, cluster, datacenter, clientIp, release.getAppId(), release.getClusterName(),
instanceConfigAuditUtil.audit(appId, cluster, datacenter, clientIp, release.getAppId(),
release.getClusterName(),
release.getNamespaceName(), release.getReleaseKey());
}
}
......
package com.ctrip.framework.apollo.configservice.controller;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
......@@ -14,6 +15,7 @@ import com.google.common.collect.Multimaps;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolder;
import com.ctrip.framework.apollo.biz.message.ReleaseMessageListener;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
......@@ -53,6 +55,8 @@ import javax.servlet.http.HttpServletResponse;
public class ConfigFileController implements ReleaseMessageListener {
private static final Logger logger = LoggerFactory.getLogger(ConfigFileController.class);
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
private static final Splitter X_FORWARDED_FOR_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();
private static final long MAX_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
private static final long EXPIRE_AFTER_WRITE = 30;
private final HttpHeaders propertiesResponseHeaders;
......@@ -74,6 +78,9 @@ public class ConfigFileController implements ReleaseMessageListener {
@Autowired
private WatchKeysUtil watchKeysUtil;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
public ConfigFileController() {
localCache = CacheBuilder.newBuilder()
.expireAfterWrite(EXPIRE_AFTER_WRITE, TimeUnit.MINUTES)
......@@ -157,31 +164,41 @@ public class ConfigFileController implements ReleaseMessageListener {
//strip out .properties suffix
namespace = namespaceUtil.filterNamespaceName(namespace);
//TODO add clientIp as key parts?
if (Strings.isNullOrEmpty(clientIp)) {
clientIp = tryToGetClientIp(request);
}
//1. check whether this client has gray release rules
boolean hasGrayReleaseRule = grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp,
namespace);
String cacheKey = assembleCacheKey(outputFormat, appId, clusterName, namespace, dataCenter);
//2. try to load gray release and return
if (hasGrayReleaseRule) {
Cat.logEvent("ConfigFile.Cache.GrayRelease", cacheKey);
return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp,
request, response);
}
//3. if not gray release, check weather cache exists, if exists, return
String result = localCache.getIfPresent(cacheKey);
//4. if not exists, load from ConfigController
if (Strings.isNullOrEmpty(result)) {
Cat.logEvent("ConfigFile.Cache.Miss", cacheKey);
ApolloConfig apolloConfig =
configController
.queryConfig(appId, clusterName, namespace, dataCenter, "-1", clientIp, request,
response);
result = loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp,
request, response);
if (apolloConfig == null || apolloConfig.getConfigurations() == null) {
if (result == null) {
return null;
}
switch (outputFormat) {
case PROPERTIES:
Properties properties = new Properties();
properties.putAll(apolloConfig.getConfigurations());
result = PropertiesUtil.toString(properties);
break;
case JSON:
result = gson.toJson(apolloConfig.getConfigurations());
break;
//5. Double check if this client needs to load gray release, if yes, load from db again
//This step is mainly to avoid cache pollution
if (grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, namespace)) {
Cat.logEvent("ConfigFile.Cache.GrayReleaseConflict", cacheKey);
return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp,
request, response);
}
localCache.put(cacheKey, result);
......@@ -203,6 +220,33 @@ public class ConfigFileController implements ReleaseMessageListener {
return result;
}
private String loadConfig(ConfigFileOutputFormat outputFormat, String appId, String clusterName,
String namespace, String dataCenter, String clientIp,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
ApolloConfig apolloConfig = configController.queryConfig(appId, clusterName, namespace,
dataCenter, "-1", clientIp, request, response);
if (apolloConfig == null || apolloConfig.getConfigurations() == null) {
return null;
}
String result = null;
switch (outputFormat) {
case PROPERTIES:
Properties properties = new Properties();
properties.putAll(apolloConfig.getConfigurations());
result = PropertiesUtil.toString(properties);
break;
case JSON:
result = gson.toJson(apolloConfig.getConfigurations());
break;
}
return result;
}
String assembleCacheKey(ConfigFileOutputFormat outputFormat, String appId, String clusterName,
String namespace,
String dataCenter) {
......@@ -249,4 +293,12 @@ public class ConfigFileController implements ReleaseMessageListener {
return value;
}
}
private String tryToGetClientIp(HttpServletRequest request) {
String forwardedFor = request.getHeader("X-FORWARDED-FOR");
if (!Strings.isNullOrEmpty(forwardedFor)) {
return X_FORWARDED_FOR_SPLITTER.splitToList(forwardedFor).get(0);
}
return request.getRemoteAddr();
}
}
......@@ -74,7 +74,7 @@ public class InstanceConfigAuditUtil implements InitializingBean {
//load instance config release key from cache, and check if release key is the same
String instanceConfigCacheKey = assembleInstanceConfigKey(instanceId, auditModel
.getConfigAppId(), auditModel.getConfigClusterName(), auditModel.getConfigNamespace());
.getConfigAppId(), auditModel.getConfigNamespace());
String cacheReleaseKey = instanceConfigReleaseKeyCache.getIfPresent(instanceConfigCacheKey);
//if release key is the same, then skip audit
......@@ -86,10 +86,11 @@ public class InstanceConfigAuditUtil implements InitializingBean {
//if release key is not the same or cannot find in cache, then do audit
InstanceConfig instanceConfig = instanceService.findInstanceConfig(instanceId, auditModel
.getConfigAppId(), auditModel.getConfigClusterName(), auditModel.getConfigNamespace());
.getConfigAppId(), auditModel.getConfigNamespace());
if (instanceConfig != null) {
if (!Objects.equals(instanceConfig.getReleaseKey(), auditModel.getReleaseKey())) {
instanceConfig.setConfigClusterName(auditModel.getConfigClusterName());
instanceConfig.setReleaseKey(auditModel.getReleaseKey());
instanceConfig.setReleaseDeliveryTime(auditModel.getOfferTime());
}
......@@ -164,10 +165,8 @@ public class InstanceConfigAuditUtil implements InitializingBean {
return STRING_JOINER.join(keyParts);
}
private String assembleInstanceConfigKey(long instanceId, String configAppId, String
configClusterName,
String configNamespace) {
return STRING_JOINER.join(instanceId, configAppId, configClusterName, configNamespace);
private String assembleInstanceConfigKey(long instanceId, String configAppId, String configNamespace) {
return STRING_JOINER.join(instanceId, configAppId, configNamespace);
}
public static class InstanceConfigAuditModel {
......
......@@ -6,6 +6,7 @@ import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolder;
import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.biz.entity.Release;
......@@ -63,6 +64,8 @@ public class ConfigControllerTest {
@Mock
private InstanceConfigAuditUtil instanceConfigAuditUtil;
@Mock
private GrayReleaseRulesHolder grayReleaseRulesHolder;
@Mock
private HttpServletRequest someRequest;
@Before
......@@ -72,6 +75,7 @@ public class ConfigControllerTest {
ReflectionTestUtils.setField(configController, "appNamespaceService", appNamespaceService);
ReflectionTestUtils.setField(configController, "namespaceUtil", namespaceUtil);
ReflectionTestUtils.setField(configController, "instanceConfigAuditUtil", instanceConfigAuditUtil);
ReflectionTestUtils.setField(configController, "grayReleaseRulesHolder", grayReleaseRulesHolder);
someAppId = "1";
someClusterName = "someClusterName";
......@@ -90,6 +94,8 @@ public class ConfigControllerTest {
when(namespaceUtil.filterNamespaceName(defaultNamespaceName)).thenReturn(defaultNamespaceName);
when(namespaceUtil.filterNamespaceName(somePublicNamespaceName))
.thenReturn(somePublicNamespaceName);
when(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(anyString(), anyString(),
anyString(), anyString(), anyString())).thenReturn(null);
}
@Test
......@@ -116,6 +122,45 @@ public class ConfigControllerTest {
someClientIp, someAppId, someClusterName, defaultNamespaceName, someServerSideNewReleaseKey);
}
@Test
public void testQueryConfigWithGrayRelease() throws Exception {
String someClientSideReleaseKey = "1";
String someServerSideNewReleaseKey = "2";
String someServerSideGrayReleaseKey = "3";
HttpServletResponse someResponse = mock(HttpServletResponse.class);
Release grayRelease = mock(Release.class);
long grayReleaseId = 999;
String someGrayConfiguration = "{\"apollo.bar\": \"foo_gray\"}";
when(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(someAppId, someClientIp,
someAppId, someClusterName, defaultNamespaceName)).thenReturn(grayReleaseId);
when(releaseService.findActiveOne(grayReleaseId)).thenReturn(grayRelease);
when(releaseService.findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName))
.thenReturn(someRelease);
when(someRelease.getReleaseKey()).thenReturn(someServerSideNewReleaseKey);
when(grayRelease.getAppId()).thenReturn(someAppId);
when(grayRelease.getClusterName()).thenReturn(someClusterName);
when(grayRelease.getReleaseKey()).thenReturn(someServerSideGrayReleaseKey);
when(grayRelease.getNamespaceName()).thenReturn(defaultNamespaceName);
when(grayRelease.getConfigurations()).thenReturn(someGrayConfiguration);
ApolloConfig result = configController.queryConfig(someAppId, someClusterName,
defaultNamespaceName, someDataCenter, someClientSideReleaseKey,
someClientIp, someRequest, someResponse);
verify(releaseService, times(1)).findActiveOne(grayReleaseId);
verify(releaseService, never()).findLatestActiveRelease(someAppId, someClusterName, defaultNamespaceName);
assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getCluster());
assertEquals(defaultNamespaceName, result.getNamespaceName());
assertEquals(someServerSideGrayReleaseKey, result.getReleaseKey());
verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter,
someClientIp, someAppId, someClusterName, defaultNamespaceName, someServerSideGrayReleaseKey);
}
@Test
public void testQueryConfigFile() throws Exception {
String someClientSideReleaseKey = "1";
......
......@@ -9,6 +9,7 @@ import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.grayReleaseRule.GrayReleaseRulesHolder;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.configservice.util.WatchKeysUtil;
......@@ -33,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
......@@ -49,6 +51,8 @@ public class ConfigFileControllerTest {
private WatchKeysUtil watchKeysUtil;
@Mock
private NamespaceUtil namespaceUtil;
@Mock
private GrayReleaseRulesHolder grayReleaseRulesHolder;
private ConfigFileController configFileController;
private String someAppId;
private String someClusterName;
......@@ -68,6 +72,7 @@ public class ConfigFileControllerTest {
ReflectionTestUtils.setField(configFileController, "configController", configController);
ReflectionTestUtils.setField(configFileController, "watchKeysUtil", watchKeysUtil);
ReflectionTestUtils.setField(configFileController, "namespaceUtil", namespaceUtil);
ReflectionTestUtils.setField(configFileController, "grayReleaseRulesHolder", grayReleaseRulesHolder);
someAppId = "someAppId";
someClusterName = "someClusterName";
......@@ -76,6 +81,8 @@ public class ConfigFileControllerTest {
someClientIp = "10.1.1.1";
when(namespaceUtil.filterNamespaceName(someNamespace)).thenReturn(someNamespace);
when(grayReleaseRulesHolder.hasGrayReleaseRule(anyString(), anyString(), anyString()))
.thenReturn(false);
watchedKeys2CacheKey =
(Multimap<String, String>) ReflectionTestUtils
......@@ -169,6 +176,45 @@ public class ConfigFileControllerTest {
assertEquals(configurations, gson.fromJson(response.getBody(), responseType));
}
@Test
public void testQueryConfigWithGrayRelease() throws Exception {
String someKey = "someKey";
String someValue = "someValue";
Gson gson = new Gson();
Type responseType = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> configurations =
ImmutableMap.of(someKey, someValue);
when(grayReleaseRulesHolder.hasGrayReleaseRule(someAppId, someClientIp, someNamespace))
.thenReturn(true);
ApolloConfig someApolloConfig = mock(ApolloConfig.class);
when(someApolloConfig.getConfigurations()).thenReturn(configurations);
when(configController
.queryConfig(someAppId, someClusterName, someNamespace, someDataCenter, "-1", someClientIp,
someRequest, someResponse)).thenReturn(someApolloConfig);
ResponseEntity<String> response =
configFileController
.queryConfigAsJson(someAppId, someClusterName, someNamespace, someDataCenter,
someClientIp, someRequest, someResponse);
ResponseEntity<String> anotherResponse =
configFileController
.queryConfigAsJson(someAppId, someClusterName, someNamespace, someDataCenter,
someClientIp, someRequest, someResponse);
verify(configController, times(2))
.queryConfig(someAppId, someClusterName, someNamespace, someDataCenter, "-1", someClientIp,
someRequest, someResponse);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(configurations, gson.fromJson(response.getBody(), responseType));
assertTrue(watchedKeys2CacheKey.isEmpty());
assertTrue(cacheKey2WatchedKeys.isEmpty());
}
@Test
public void testHandleMessage() throws Exception {
String someWatchKey = "someWatchKey";
......
package com.ctrip.framework.apollo.configservice.integration;
import com.google.common.base.Joiner;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
......@@ -10,6 +12,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.web.client.HttpStatusCodeException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertEquals;
/**
......@@ -17,20 +24,26 @@ import static org.junit.Assert.assertEquals;
*/
public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest {
private String someAppId;
private String somePublicAppId;
private String someCluster;
private String someNamespace;
private String somePublicNamespace;
private String someDC;
private String someDefaultCluster;
private String someClientIp;
private ExecutorService executorService;
@Before
public void setUp() throws Exception {
someAppId = "someAppId";
someCluster = "someCluster";
someNamespace = "someNamespace";
somePublicAppId = "somePublicAppId";
somePublicNamespace = "somePublicNamespace";
someDC = "someDC";
someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
someClientIp = "1.1.1.1";
executorService = Executors.newSingleThreadExecutor();
}
@Test
......@@ -47,6 +60,29 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("v1", result.getConfigurations().get("k1"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-gray-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testQueryGrayConfigWithDefaultClusterAndDefaultNamespaceOK() throws Exception {
AtomicBoolean stop = new AtomicBoolean();
periodicSendMessage(executorService, assembleKey(someAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, ConfigConsts.NAMESPACE_APPLICATION),
stop);
TimeUnit.MILLISECONDS.sleep(500);
stop.set(true);
ResponseEntity<ApolloConfig> response = restTemplate
.getForEntity("{baseurl}/configs/{appId}/{clusterName}/{namespace}?ip={clientIp}", ApolloConfig.class,
getHostUrl(), someAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, ConfigConsts.NAMESPACE_APPLICATION, someClientIp);
ApolloConfig result = response.getBody();
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("TEST-GRAY-RELEASE-KEY1", result.getReleaseKey());
assertEquals("v1-gray", result.getConfigurations().get("k1"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......@@ -119,6 +155,30 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals(HttpStatus.NOT_MODIFIED, response.getStatusCode());
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-gray-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testQueryPublicGrayConfigWithNoOverride() throws Exception {
AtomicBoolean stop = new AtomicBoolean();
periodicSendMessage(executorService, assembleKey(somePublicAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, somePublicNamespace),
stop);
TimeUnit.MILLISECONDS.sleep(500);
stop.set(true);
ResponseEntity<ApolloConfig> response = restTemplate
.getForEntity("{baseurl}/configs/{appId}/{clusterName}/{namespace}?ip={clientIp}", ApolloConfig.class,
getHostUrl(), someAppId, someCluster, somePublicNamespace, someClientIp);
ApolloConfig result = response.getBody();
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("TEST-GRAY-RELEASE-KEY2", result.getReleaseKey());
assertEquals("gray-v1", result.getConfigurations().get("k1"));
assertEquals("gray-v2", result.getConfigurations().get("k2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......@@ -199,6 +259,33 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("default-v2", result.getConfigurations().get("k2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-release-public-default-override.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-gray-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testQueryPublicGrayConfigWithOverride() throws Exception {
AtomicBoolean stop = new AtomicBoolean();
periodicSendMessage(executorService, assembleKey(somePublicAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, somePublicNamespace),
stop);
TimeUnit.MILLISECONDS.sleep(500);
stop.set(true);
ResponseEntity<ApolloConfig> response = restTemplate
.getForEntity("{baseurl}/configs/{appId}/{clusterName}/{namespace}?ip={clientIp}", ApolloConfig.class,
getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, someClientIp);
ApolloConfig result = response.getBody();
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(
"TEST-RELEASE-KEY5" + ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR + "TEST-GRAY-RELEASE-KEY2",
result.getReleaseKey());
assertEquals("override-v1", result.getConfigurations().get("k1"));
assertEquals("gray-v2", result.getConfigurations().get("k2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......@@ -250,4 +337,8 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("someDC-v1", result.getConfigurations().get("k1"));
assertEquals("someDC-v2", result.getConfigurations().get("k2"));
}
private String assembleKey(String appId, String cluster, String namespace) {
return Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(appId, cluster, namespace);
}
}
package com.ctrip.framework.apollo.configservice.integration;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
......@@ -18,9 +19,13 @@ import org.springframework.test.context.jdbc.Sql;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
......@@ -28,22 +33,30 @@ import static org.junit.Assert.assertTrue;
*/
public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegrationTest {
private String someAppId;
private String somePublicAppId;
private String someCluster;
private String someNamespace;
private String somePublicNamespace;
private String someDC;
private String someDefaultCluster;
private String grayClientIp;
private String nonGrayClientIp;
private Gson gson = new Gson();
private ExecutorService executorService;
private Type mapResponseType = new TypeToken<Map<String, String>>(){}.getType();
@Before
public void setUp() throws Exception {
someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
someAppId = "someAppId";
somePublicAppId = "somePublicAppId";
someCluster = "someCluster";
someNamespace = "someNamespace";
somePublicNamespace = "somePublicNamespace";
someDC = "someDC";
grayClientIp = "1.1.1.1";
nonGrayClientIp = "2.2.2.2";
executorService = Executors.newSingleThreadExecutor();
}
@Test
......@@ -61,6 +74,40 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertTrue(result.contains("k2=v2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-gray-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testQueryConfigAsPropertiesWithGrayRelease() throws Exception {
AtomicBoolean stop = new AtomicBoolean();
periodicSendMessage(executorService, assembleKey(someAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, ConfigConsts.NAMESPACE_APPLICATION),
stop);
TimeUnit.MILLISECONDS.sleep(500);
stop.set(true);
ResponseEntity<String> response =
restTemplate
.getForEntity("{baseurl}/configfiles/{appId}/{clusterName}/{namespace}?ip={clientIp}", String.class,
getHostUrl(), someAppId, someDefaultCluster, ConfigConsts.NAMESPACE_APPLICATION, grayClientIp);
ResponseEntity<String> anotherResponse =
restTemplate
.getForEntity("{baseurl}/configfiles/{appId}/{clusterName}/{namespace}?ip={clientIp}", String.class,
getHostUrl(), someAppId, someDefaultCluster, ConfigConsts.NAMESPACE_APPLICATION, nonGrayClientIp);
String result = response.getBody();
String anotherResult = anotherResponse.getBody();
assertEquals(HttpStatus.OK, response.getStatusCode());
assertTrue(result.contains("k1=v1-gray"));
assertEquals(HttpStatus.OK, anotherResponse.getStatusCode());
assertFalse(anotherResult.contains("k1=v1-gray"));
assertTrue(anotherResult.contains("k1=v1"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-release-public-dc-override.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
......@@ -89,7 +136,6 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
.getForEntity("{baseurl}/configfiles/json/{appId}/{clusterName}/{namespace}", String.class,
getHostUrl(), someAppId, someCluster, someNamespace);
String result = response.getBody();
Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType);
assertEquals(HttpStatus.OK, response.getStatusCode());
......@@ -108,7 +154,6 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
String.class,
getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, someDC);
String result = response.getBody();
Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType);
assertEquals(HttpStatus.OK, response.getStatusCode());
......@@ -116,6 +161,47 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertEquals("someDC-v2", configs.get("k2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-release-public-default-override.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/test-gray-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void testQueryPublicConfigAsJsonWithGrayRelease() throws Exception {
AtomicBoolean stop = new AtomicBoolean();
periodicSendMessage(executorService, assembleKey(somePublicAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, somePublicNamespace),
stop);
TimeUnit.MILLISECONDS.sleep(500);
stop.set(true);
ResponseEntity<String> response =
restTemplate
.getForEntity(
"{baseurl}/configfiles/json/{appId}/{clusterName}/{namespace}?ip={clientIp}",
String.class,
getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, grayClientIp);
ResponseEntity<String> anotherResponse =
restTemplate
.getForEntity(
"{baseurl}/configfiles/json/{appId}/{clusterName}/{namespace}?ip={clientIp}",
String.class,
getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, nonGrayClientIp);
Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType);
Map<String, String> anotherConfigs = gson.fromJson(anotherResponse.getBody(), mapResponseType);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals(HttpStatus.OK, anotherResponse.getStatusCode());
assertEquals("override-v1", configs.get("k1"));
assertEquals("gray-v2", configs.get("k2"));
assertEquals("override-v1", anotherConfigs.get("k1"));
assertEquals("default-v2", anotherConfigs.get("k2"));
}
@Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
......@@ -166,4 +252,7 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertTrue(result.contains("k2=v2-changed"));
}
private String assembleKey(String appId, String cluster, String namespace) {
return Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).join(appId, cluster, namespace);
}
}
......@@ -13,12 +13,9 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.function.ObjDoubleConsumer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
......@@ -60,13 +57,13 @@ public class InstanceConfigAuditUtilTest {
someDataCenter = "someDataCenter";
someIp = "someIp";
someConfigAppId = "someConfigAppId";
someConfigClusterName= "someConfigClusterName";
someConfigClusterName = "someConfigClusterName";
someConfigNamespace = "someConfigNamespace";
someReleaseKey = "someReleaseKey";
someAuditModel = new InstanceConfigAuditUtil.InstanceConfigAuditModel(someAppId,
someClusterName, someDataCenter, someIp, someConfigAppId, someConfigClusterName, someConfigNamespace,
someReleaseKey);
someClusterName, someDataCenter, someIp, someConfigAppId, someConfigClusterName,
someConfigNamespace, someReleaseKey);
}
@Test
......@@ -93,7 +90,7 @@ public class InstanceConfigAuditUtilTest {
verify(instanceService, times(1)).findInstance(someAppId, someClusterName, someDataCenter,
someIp);
verify(instanceService, times(1)).createInstance(any(Instance.class));
verify(instanceService, times(1)).findInstanceConfig(someInstanceId, someConfigAppId, someConfigClusterName,
verify(instanceService, times(1)).findInstanceConfig(someInstanceId, someConfigAppId,
someConfigNamespace);
verify(instanceService, times(1)).createInstanceConfig(any(InstanceConfig.class));
}
......
......@@ -18,4 +18,4 @@ management:
enabled: false
health:
status:
order: DOWN, OUT_OF_SERVICE, UNKNOWN, UP
\ No newline at end of file
order: DOWN, OUT_OF_SERVICE, UNKNOWN, UP
......@@ -4,4 +4,6 @@ DELETE FROM AppNamespace;
DELETE FROM Cluster;
DELETE FROM App;
DELETE FROM ReleaseMessage;
DELETE FROM GrayReleaseRule;
INSERT INTO GrayReleaseRule (`Id`, `AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)
VALUES
(1, 'someAppId', 'default', 'application', 'gray-branch-1', '[{"clientAppId":"someAppId","clientIpList":["1.1.1.1"]}]', 986, 1);
INSERT INTO GrayReleaseRule (`Id`, `AppId`, `ClusterName`, `NamespaceName`, `BranchName`, `Rules`, `ReleaseId`, `BranchStatus`)
VALUES
(2, 'somePublicAppId', 'default', 'somePublicNamespace', 'gray-branch-2', '[{"clientAppId":"someAppId","clientIpList":["1.1.1.1"]}]', 985, 1);
......@@ -37,3 +37,7 @@ INSERT INTO RELEASE (id, ReleaseKey, Name, Comment, AppId, ClusterName, Namespac
VALUES (988, 'TEST-RELEASE-KEY6', 'INTEGRATION-TEST-PRIVATE-CONFIG-FILE','First Release','someAppId', 'default', 'anotherNamespace', '{"k1":"v1-file"}');
INSERT INTO RELEASE (id, ReleaseKey, Name, Comment, AppId, ClusterName, NamespaceName, Configurations)
VALUES (987, 'TEST-RELEASE-KEY7', 'INTEGRATION-TEST-PUBLIC-CONFIG-FILE','First Release','somePublicAppId', 'default', 'anotherNamespace', '{"k2":"v2-file"}');
INSERT INTO RELEASE (id, ReleaseKey, Name, Comment, AppId, ClusterName, NamespaceName, Configurations)
VALUES (986, 'TEST-GRAY-RELEASE-KEY1', 'INTEGRATION-TEST-DEFAULT','Gray Release','someAppId', 'gray-branch-1', 'application', '{"k1":"v1-gray"}');
INSERT INTO RELEASE (id, ReleaseKey, Name, Comment, AppId, ClusterName, NamespaceName, Configurations)
VALUES (985, 'TEST-GRAY-RELEASE-KEY2', 'INTEGRATION-TEST-NAMESPACE','Gray Release','somePublicAppId', 'gray-branch-2', 'somePublicNamespace', '{"k1":"gray-v1", "k2":"gray-v2"}');
\ No newline at end of file
......@@ -8,7 +8,7 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.service.ReleaseService;
import com.ctrip.framework.apollo.portal.service.UserService;
......@@ -55,7 +55,7 @@ public class ReleaseController {
model.setClusterName(clusterName);
model.setNamespaceName(namespaceName);
return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.createRelease(model));
return OpenApiBeanUtils.transformFromReleaseDTO(releaseService.publish(model));
}
@RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest", method = RequestMethod.GET)
......
......@@ -139,7 +139,14 @@ public class RetryableRestTemplate {
} catch (Throwable t) {
logger.error("Http request failed, uri: {}, method: {}", uri, HttpMethod.GET, t);
Cat.logError(t);
Cat.logEvent(CatEventType.API_RETRY, uri);
if (canRetry(t, HttpMethod.GET)){
Cat.logEvent(CatEventType.API_RETRY, uri);
}else {// biz exception rethrow
ct.setStatus(t);
ct.complete();
throw t;
}
}
}
......
......@@ -52,9 +52,9 @@ public class AuthConfiguration {
@Bean
public ServletListenerRegistrationBean redisAppSettingListner() {
ServletListenerRegistrationBean redisAppSettingListner = new ServletListenerRegistrationBean();
redisAppSettingListner.setListener(listener("org.jasig.cas.client.credis.CRedisAppSettingListner"));
return redisAppSettingListner;
ServletListenerRegistrationBean redisAppSettingListener = new ServletListenerRegistrationBean();
redisAppSettingListener.setListener(listener("org.jasig.cas.client.credis.CRedisAppSettingListner"));
return redisAppSettingListener;
}
@Bean
......
......@@ -20,4 +20,11 @@ public interface CatEventType {
String USER_ACCESS = "User.Access";
String CREATE_GRAY_RELEASE = "GrayRelease.Create";
String DELETE_GRAY_RELEASE = "GrayRelease.Delete";
String MERGE_GRAY_RELEASE = "GrayRelease.Merge";
String UPDATE_GRAY_RELEASE_RULE = "GrayReleaseRule.Update";
}
......@@ -60,7 +60,6 @@ public class AppController {
}
@RequestMapping("/by-owner")
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page){
return appService.findByOwnerName(owner, page);
......
......@@ -5,8 +5,8 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceSyncModel;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceTextModel;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceSyncModel;
import com.ctrip.framework.apollo.portal.entity.model.NamespaceTextModel;
import com.ctrip.framework.apollo.portal.entity.vo.ItemDiffs;
import com.ctrip.framework.apollo.portal.service.ItemService;
......@@ -107,6 +107,15 @@ public class ItemController {
return items;
}
@RequestMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/items")
public List<ItemDTO> findBranchItems(@PathVariable("appId") String appId, @PathVariable String env,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
@PathVariable("branchName") String branchName) {
return findItems(appId, env, branchName, namespaceName, "lastModifiedTime");
}
@RequestMapping(value = "/namespaces/{namespaceName}/diff", method = RequestMethod.POST, consumes = {
"application/json"})
public List<ItemDiffs> diff(@RequestBody NamespaceSyncModel model) {
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册