Commit 9ca8b170 authored by lepdou's avatar lepdou

abtest

parent f0b72c35
......@@ -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;
}
}
......@@ -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');
This diff is collapsed.
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) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment