Commit 9ca8b170 authored by lepdou's avatar lepdou

abtest

parent f0b72c35
...@@ -25,16 +25,24 @@ public class ClusterController { ...@@ -25,16 +25,24 @@ public class ClusterController {
private ClusterService clusterService; private ClusterService clusterService;
@RequestMapping(path = "/apps/{appId}/clusters", method = RequestMethod.POST) @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())) { if (!InputValidator.isValidClusterNamespace(dto.getName())) {
throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE)); throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
} }
Cluster entity = BeanUtils.transfrom(Cluster.class, dto); Cluster entity = BeanUtils.transfrom(Cluster.class, dto);
Cluster managedEntity = clusterService.findOne(appId, entity.getName()); Cluster managedEntity = clusterService.findOne(appId, entity.getName());
if (managedEntity != null) { if (managedEntity != null) {
throw new BadRequestException("cluster already exist."); 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); dto = BeanUtils.transfrom(ClusterDTO.class, entity);
return dto; return dto;
...@@ -52,7 +60,7 @@ public class ClusterController { ...@@ -52,7 +60,7 @@ public class ClusterController {
@RequestMapping("/apps/{appId}/clusters") @RequestMapping("/apps/{appId}/clusters")
public List<ClusterDTO> find(@PathVariable("appId") String appId) { 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); return BeanUtils.batchTransform(ClusterDTO.class, clusters);
} }
......
package com.ctrip.framework.apollo.adminservice.controller; package com.ctrip.framework.apollo.adminservice.controller;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
...@@ -159,12 +160,20 @@ public class InstanceConfigController { ...@@ -159,12 +160,20 @@ public class InstanceConfigController {
} }
@RequestMapping(value = "/by-namespace", method = RequestMethod.GET) @RequestMapping(value = "/by-namespace", method = RequestMethod.GET)
public PageDTO<InstanceDTO> getInstancesByNamespace(@RequestParam("appId") String appId, public PageDTO<InstanceDTO> getInstancesByNamespace(
@RequestParam("clusterName") String clusterName, @RequestParam("appId") String appId, @RequestParam("clusterName") String clusterName,
@RequestParam("namespaceName") String @RequestParam("namespaceName") String namespaceName,
namespaceName, Pageable pageable) { @RequestParam(value = "instanceAppId", required = false) String instanceAppId,
Page<Instance> instances = instanceService.findInstancesByNamespace(appId, clusterName, Pageable pageable) {
Page<Instance> instances;
if (Strings.isNullOrEmpty(instanceAppId)) {
instances = instanceService.findInstancesByNamespace(appId, clusterName,
namespaceName, pageable); namespaceName, pageable);
} else {
instances = instanceService.findInstancesByNamespaceAndInstanceAppId(instanceAppId, appId,
clusterName, namespaceName, pageable);
}
List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances.getContent()); List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances.getContent());
return new PageDTO<>(instanceDTOs, pageable, instances.getTotalElements()); return new PageDTO<>(instanceDTOs, pageable, instances.getTotalElements());
} }
......
...@@ -45,13 +45,6 @@ public class ItemController { ...@@ -45,13 +45,6 @@ public class ItemController {
if (managedEntity != null) { if (managedEntity != null) {
throw new BadRequestException("item already exist"); throw new BadRequestException("item already exist");
} else { } 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); entity = itemService.save(entity);
builder.createItem(entity); builder.createItem(entity);
} }
...@@ -157,4 +150,6 @@ public class ItemController { ...@@ -157,4 +150,6 @@ public class ItemController {
} }
return BeanUtils.transfrom(ItemDTO.class, item); 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; 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.Namespace;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.message.MessageSender; import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics; 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.NamespaceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService; 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.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.common.exception.NotFoundException; import com.ctrip.framework.apollo.common.exception.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable; 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.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController @RestController
public class ReleaseController { public class ReleaseController {
private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();
@Autowired @Autowired
private ReleaseService releaseService; private ReleaseService releaseService;
@Autowired @Autowired
private NamespaceService namespaceService; private NamespaceService namespaceService;
@Autowired @Autowired
private MessageSender messageSender; private MessageSender messageSender;
@Autowired
private NamespaceBranchService namespaceBranchService;
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
@RequestMapping("/releases/{releaseId}") @RequestMapping("/releases/{releaseId}")
public ReleaseDTO get(@PathVariable("releaseId") long releaseId) { public ReleaseDTO get(@PathVariable("releaseId") long releaseId) {
...@@ -46,6 +56,16 @@ public class ReleaseController { ...@@ -46,6 +56,16 @@ public class ReleaseController {
return BeanUtils.transfrom(ReleaseDTO.class, release); 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") @RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseDTO> findAllReleases(@PathVariable("appId") String appId, public List<ReleaseDTO> findAllReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @PathVariable("clusterName") String clusterName,
...@@ -74,21 +94,66 @@ public class ReleaseController { ...@@ -74,21 +94,66 @@ public class ReleaseController {
} }
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST) @RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
public ReleaseDTO buildRelease(@PathVariable("appId") String appId, public ReleaseDTO publish(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName, @PathVariable("namespaceName") String namespaceName,
@RequestParam("name") String name, @RequestParam("name") String releaseName,
@RequestParam(name = "comment", required = false) String comment, @RequestParam(name = "comment", required = false) String releaseComment,
@RequestParam("operator") String operator) { @RequestParam("operator") String operator) {
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName); Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId, throw new NotFoundException(String.format("Could not find namespace for %s %s %s", appId,
clusterName, namespaceName)); clusterName, namespaceName));
} }
Release release = releaseService.buildRelease(name, comment, namespace, operator); Release release = releaseService.publish(namespace, releaseName, releaseComment, operator);
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
//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.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); Topics.APOLLO_RELEASE_TOPIC);
return BeanUtils.transfrom(ReleaseDTO.class, release); return BeanUtils.transfrom(ReleaseDTO.class, release);
} }
@RequestMapping(path = "/releases/{releaseId}/rollback", method = RequestMethod.PUT) @RequestMapping(path = "/releases/{releaseId}/rollback", method = RequestMethod.PUT)
...@@ -101,11 +166,8 @@ public class ReleaseController { ...@@ -101,11 +166,8 @@ public class ReleaseController {
String clusterName = release.getClusterName(); String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName(); String namespaceName = release.getNamespaceName();
//send release message //send release message
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName), messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC); 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: ...@@ -13,6 +13,8 @@ endpoints:
health: health:
sensitive: false sensitive: false
management: management:
security: security:
enabled: false enabled: false
......
...@@ -243,7 +243,7 @@ public class InstanceConfigControllerTest { ...@@ -243,7 +243,7 @@ public class InstanceConfigControllerTest {
pageable)).thenReturn(instances); pageable)).thenReturn(instances);
PageDTO<InstanceDTO> result = instanceConfigController.getInstancesByNamespace(someAppId, PageDTO<InstanceDTO> result = instanceConfigController.getInstancesByNamespace(someAppId,
someClusterName, someNamespaceName, pageable); someClusterName, someNamespaceName, null, pageable);
assertEquals(2, result.getContent().size()); assertEquals(2, result.getContent().size());
InstanceDTO someInstanceDto = null; InstanceDTO someInstanceDto = null;
...@@ -261,6 +261,47 @@ public class InstanceConfigControllerTest { ...@@ -261,6 +261,47 @@ public class InstanceConfigControllerTest {
verifyInstance(anotherInstance, anotherInstanceDto); 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 @Test
public void testGetInstancesCountByNamespace() throws Exception { public void testGetInstancesCountByNamespace() throws Exception {
String someAppId = "someAppId"; String someAppId = "someAppId";
......
...@@ -121,7 +121,7 @@ public class ReleaseControllerTest extends AbstractControllerTest { ...@@ -121,7 +121,7 @@ public class ReleaseControllerTest extends AbstractControllerTest {
.thenReturn(someNamespace); .thenReturn(someNamespace);
releaseController releaseController
.buildRelease(someAppId, someCluster, someNamespaceName, someName, someComment, "test"); .publish(someAppId, someCluster, someNamespaceName, someName, someComment, "test");
verify(someMessageSender, times(1)) verify(someMessageSender, times(1))
.sendMessage(Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR) .sendMessage(Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
......
...@@ -6,3 +6,6 @@ logging: ...@@ -6,3 +6,6 @@ logging:
level: level:
org.springframework.cloud: 'DEBUG' org.springframework.cloud: 'DEBUG'
file: /opt/logs/100003171/apollo-assembly.log file: /opt/logs/100003171/apollo-assembly.log
...@@ -24,6 +24,9 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> { ...@@ -24,6 +24,9 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> {
@Column(name = "AppId", nullable = false) @Column(name = "AppId", nullable = false)
private String appId; private String appId;
@Column(name = "ParentClusterId", nullable = false)
private long parentClusterId;
public String getAppId() { public String getAppId() {
return appId; return appId;
} }
...@@ -40,8 +43,17 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> { ...@@ -40,8 +43,17 @@ public class Cluster extends BaseEntity implements Comparable<Cluster> {
this.name = name; this.name = name;
} }
public long getParentClusterId() {
return parentClusterId;
}
public void setParentClusterId(long parentClusterId) {
this.parentClusterId = parentClusterId;
}
public String toString() { 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 @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; ...@@ -9,7 +9,11 @@ import java.util.List;
public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> { public interface ClusterRepository extends PagingAndSortingRepository<Cluster, Long> {
List<Cluster> findByAppIdAndParentClusterId(String appId, Long parentClusterId);
List<Cluster> findByAppId(String appId); List<Cluster> findByAppId(String appId);
Cluster findByAppIdAndName(String appId, String name); 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; ...@@ -4,7 +4,9 @@ import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
...@@ -12,8 +14,8 @@ import java.util.Set; ...@@ -12,8 +14,8 @@ import java.util.Set;
public interface InstanceConfigRepository extends PagingAndSortingRepository<InstanceConfig, Long> { public interface InstanceConfigRepository extends PagingAndSortingRepository<InstanceConfig, Long> {
InstanceConfig findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName(long instanceId, String InstanceConfig findByInstanceIdAndConfigAppIdAndConfigNamespaceName(long instanceId, String
configAppId, String configClusterName, String configNamespaceName); configAppId, String configNamespaceName);
Page<InstanceConfig> findByReleaseKeyAndDataChangeLastModifiedTimeAfter(String releaseKey, Date Page<InstanceConfig> findByReleaseKeyAndDataChangeLastModifiedTimeAfter(String releaseKey, Date
validDate, Pageable pageable); validDate, Pageable pageable);
...@@ -24,4 +26,18 @@ public interface InstanceConfigRepository extends PagingAndSortingRepository<Ins ...@@ -24,4 +26,18 @@ public interface InstanceConfigRepository extends PagingAndSortingRepository<Ins
List<InstanceConfig> findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfterAndReleaseKeyNotIn( List<InstanceConfig> findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfterAndReleaseKeyNotIn(
String appId, String clusterName, String namespaceName, Date validDate, Set<String> releaseKey); 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 ...@@ -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") @Query("update Namespace set isdeleted=1,DataChange_LastModifiedBy = ?3 where appId=?1 and clusterName=?2")
int batchDelete(String appId, String clusterName, String operator); 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 ...@@ -19,13 +19,20 @@ public interface ReleaseRepository extends PagingAndSortingRepository<Release, L
Release findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(@Param("appId") String appId, @Param("clusterName") String clusterName, Release findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(@Param("appId") String appId, @Param("clusterName") String clusterName,
@Param("namespaceName") String namespaceName); @Param("namespaceName") String namespaceName);
Release findByIdAndIsAbandonedFalse(long id);
List<Release> findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page); List<Release> findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(String appId, String clusterName, String namespaceName, Pageable page);
List<Release> findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(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> findByReleaseKeyIn(Set<String> releaseKey);
List<Release> findByIdIn(Set<Long> releaseIds);
@Modifying @Modifying
@Query("update Release set isdeleted=1,DataChange_LastModifiedBy = ?4 where appId=?1 and clusterName=?2 and namespaceName = ?3") @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); 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 { ...@@ -122,7 +122,7 @@ public class AppNamespaceService {
} }
private void linkPrivateAppNamespaceInAllCluster(String appId, String namespaceName, String createBy) { 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) { for (Cluster cluster : clusters) {
Namespace namespace = new Namespace(); Namespace namespace = new Namespace();
namespace.setClusterName(cluster.getName()); namespace.setClusterName(cluster.getName());
......
...@@ -39,12 +39,16 @@ public class ClusterService { ...@@ -39,12 +39,16 @@ public class ClusterService {
return clusterRepository.findByAppIdAndName(appId, name); 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)) { if (Strings.isNullOrEmpty(appId)) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<Cluster> clusters = clusterRepository.findByAppId(appId); List<Cluster> clusters = clusterRepository.findByAppIdAndParentClusterId(appId, 0L);
if (clusters == null) { if (clusters == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
...@@ -55,15 +59,24 @@ public class ClusterService { ...@@ -55,15 +59,24 @@ public class ClusterService {
} }
@Transactional @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())) { if (!isClusterNameUnique(entity.getAppId(), entity.getName())) {
throw new ServiceException("cluster not unique"); throw new BadRequestException("cluster not unique");
} }
entity.setId(0);//protection entity.setId(0);//protection
Cluster cluster = clusterRepository.save(entity); Cluster cluster = clusterRepository.save(entity);
namespaceService.createPrivateNamespace(cluster.getAppId(), cluster.getName(), cluster.getDataChangeCreatedBy());
auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT, auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT,
cluster.getDataChangeCreatedBy()); cluster.getDataChangeCreatedBy());
...@@ -114,4 +127,14 @@ public class ClusterService { ...@@ -114,4 +127,14 @@ public class ClusterService {
auditService.audit(Cluster.class.getSimpleName(), cluster.getId(), Audit.OP.INSERT, createBy); 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; package com.ctrip.framework.apollo.biz.service;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Interner;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.biz.entity.Instance; import com.ctrip.framework.apollo.biz.entity.Instance;
...@@ -16,6 +17,7 @@ import org.springframework.stereotype.Service; ...@@ -16,6 +17,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.math.BigInteger;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
...@@ -55,10 +57,10 @@ public class InstanceService { ...@@ -55,10 +57,10 @@ public class InstanceService {
} }
public InstanceConfig findInstanceConfig(long instanceId, String configAppId, String public InstanceConfig findInstanceConfig(long instanceId, String configAppId, String
configClusterName, String configNamespaceName) { configNamespaceName) {
return instanceConfigRepository return instanceConfigRepository
.findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName( .findByInstanceIdAndConfigAppIdAndConfigNamespaceName(
instanceId, configAppId, configClusterName, configNamespaceName); instanceId, configAppId, configNamespaceName);
} }
public Page<InstanceConfig> findActiveInstanceConfigsByReleaseKey(String releaseKey, Pageable public Page<InstanceConfig> findActiveInstanceConfigsByReleaseKey(String releaseKey, Pageable
...@@ -85,6 +87,42 @@ public class InstanceService { ...@@ -85,6 +87,42 @@ public class InstanceService {
return new PageImpl<>(instances, pageable, instanceConfigs.getTotalElements()); 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, public List<InstanceConfig> findInstanceConfigsByNamespaceWithReleaseKeysNotIn(String appId,
String clusterName, String clusterName,
String String
...@@ -126,6 +164,7 @@ public class InstanceService { ...@@ -126,6 +164,7 @@ public class InstanceService {
Preconditions.checkArgument(existedInstanceConfig != null, String.format( Preconditions.checkArgument(existedInstanceConfig != null, String.format(
"Instance config %d doesn't exist", instanceConfig.getId())); "Instance config %d doesn't exist", instanceConfig.getId()));
existedInstanceConfig.setConfigClusterName(instanceConfig.getConfigClusterName());
existedInstanceConfig.setReleaseKey(instanceConfig.getReleaseKey()); existedInstanceConfig.setReleaseKey(instanceConfig.getReleaseKey());
existedInstanceConfig.setReleaseDeliveryTime(instanceConfig.getReleaseDeliveryTime()); existedInstanceConfig.setReleaseDeliveryTime(instanceConfig.getReleaseDeliveryTime());
existedInstanceConfig.setDataChangeLastModifiedTime(instanceConfig existedInstanceConfig.setDataChangeLastModifiedTime(instanceConfig
......
...@@ -4,7 +4,6 @@ import com.ctrip.framework.apollo.biz.entity.Audit; ...@@ -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.Item;
import com.ctrip.framework.apollo.biz.entity.Namespace; import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.repository.ItemRepository; 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.utils.BeanUtils;
import com.ctrip.framework.apollo.common.exception.BadRequestException; import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException; import com.ctrip.framework.apollo.common.exception.NotFoundException;
...@@ -24,7 +23,7 @@ public class ItemService { ...@@ -24,7 +23,7 @@ public class ItemService {
private ItemRepository itemRepository; private ItemRepository itemRepository;
@Autowired @Autowired
private NamespaceRepository namespaceRepository; private NamespaceService namespaceService;
@Autowired @Autowired
private AuditService auditService; private AuditService auditService;
...@@ -54,8 +53,7 @@ public class ItemService { ...@@ -54,8 +53,7 @@ public class ItemService {
} }
public Item findOne(String appId, String clusterName, String namespaceName, String key) { public Item findOne(String appId, String clusterName, String namespaceName, String key) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new NotFoundException( throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName)); String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
...@@ -65,14 +63,16 @@ public class ItemService { ...@@ -65,14 +63,16 @@ public class ItemService {
} }
public Item findLastOne(String appId, String clusterName, String namespaceName) { public Item findLastOne(String appId, String clusterName, String namespaceName) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
clusterName, namespaceName);
if (namespace == null) { if (namespace == null) {
throw new NotFoundException( throw new NotFoundException(
String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName)); String.format("namespace not found for %s %s %s", appId, clusterName, namespaceName));
} }
Item item = itemRepository.findFirst1ByNamespaceIdOrderByLineNumDesc(namespace.getId()); return findLastOne(namespace.getId());
return item; }
public Item findLastOne(long namespaceId) {
return itemRepository.findFirst1ByNamespaceIdOrderByLineNumDesc(namespaceId);
} }
public Item findOne(long itemId) { public Item findOne(long itemId) {
...@@ -89,8 +89,7 @@ public class ItemService { ...@@ -89,8 +89,7 @@ public class ItemService {
} }
public List<Item> findItems(String appId, String clusterName, String namespaceName) { public List<Item> findItems(String appId, String clusterName, String namespaceName) {
Namespace namespace = namespaceRepository.findByAppIdAndClusterNameAndNamespaceName(appId, clusterName, Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
namespaceName);
if (namespace != null) { if (namespace != null) {
return findItems(namespace.getId()); return findItems(namespace.getId());
} else { } else {
...@@ -104,6 +103,13 @@ public class ItemService { ...@@ -104,6 +103,13 @@ public class ItemService {
checkItemValueLength(entity.getValue()); checkItemValueLength(entity.getValue());
entity.setId(0);//protection 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); Item item = itemRepository.save(entity);
auditService.audit(Item.class.getSimpleName(), item.getId(), Audit.OP.INSERT, auditService.audit(Item.class.getSimpleName(), item.getId(), Audit.OP.INSERT,
...@@ -125,17 +131,17 @@ public class ItemService { ...@@ -125,17 +131,17 @@ public class ItemService {
return managedItem; return managedItem;
} }
private boolean checkItemValueLength(String value){ private boolean checkItemValueLength(String value) {
int lengthLimit = Integer.valueOf(serverConfigService.getValue("item.value.length.limit", "20000")); 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); throw new BadRequestException("value too long. length limit:" + lengthLimit);
} }
return true; return true;
} }
private boolean checkItemKeyLength(String key){ private boolean checkItemKeyLength(String key) {
int lengthLimit = Integer.valueOf(serverConfigService.getValue("item.key.length.limit", "128")); 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); throw new BadRequestException("key too long. length limit:" + lengthLimit);
} }
return true; return true;
......
...@@ -8,6 +8,7 @@ import org.springframework.util.CollectionUtils; ...@@ -8,6 +8,7 @@ import org.springframework.util.CollectionUtils;
import com.ctrip.framework.apollo.biz.entity.Audit; import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Commit; import com.ctrip.framework.apollo.biz.entity.Commit;
import com.ctrip.framework.apollo.biz.entity.Item; 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.biz.utils.ConfigChangeContentBuilder;
import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets; import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
...@@ -27,6 +28,10 @@ public class ItemSetService { ...@@ -27,6 +28,10 @@ public class ItemSetService {
@Autowired @Autowired
private ItemService itemService; private ItemService itemService;
@Transactional
public ItemChangeSets updateSet(Namespace namespace, ItemChangeSets changeSets){
return updateSet(namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName(), changeSets);
}
@Transactional @Transactional
public ItemChangeSets updateSet(String appId, String clusterName, 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 { ...@@ -13,11 +13,11 @@ public class NamespaceLockService {
@Autowired @Autowired
private NamespaceLockRepository namespaceLockRepository; private NamespaceLockRepository namespaceLockRepository;
public NamespaceLock findLock(Long namespaceId){ public NamespaceLock findLock(Long namespaceId){
return namespaceLockRepository.findByNamespaceId(namespaceId); return namespaceLockRepository.findByNamespaceId(namespaceId);
} }
@Transactional @Transactional
public NamespaceLock tryLock(NamespaceLock lock){ public NamespaceLock tryLock(NamespaceLock lock){
return namespaceLockRepository.save(lock); return namespaceLockRepository.save(lock);
......
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; 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.Audit;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.entity.Namespace; import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository; import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
...@@ -30,7 +36,78 @@ public class NamespaceService { ...@@ -30,7 +36,78 @@ public class NamespaceService {
private CommitService commitService; private CommitService commitService;
@Autowired @Autowired
private ReleaseService releaseService; 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) { public boolean isNamespaceUnique(String appId, String cluster, String namespace) {
Objects.requireNonNull(appId, "AppId must not be null"); Objects.requireNonNull(appId, "AppId must not be null");
...@@ -59,7 +136,10 @@ public class NamespaceService { ...@@ -59,7 +136,10 @@ public class NamespaceService {
itemService.batchDelete(namespace.getId(), operator); itemService.batchDelete(namespace.getId(), operator);
commitService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator); commitService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator);
if (!isChildNamespace(namespace)){
releaseService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator); releaseService.batchDelete(appId, clusterName, namespace.getNamespaceName(), operator);
}
namespace.setDeleted(true); namespace.setDeleted(true);
namespace.setDataChangeLastModifiedBy(operator); namespace.setDataChangeLastModifiedBy(operator);
...@@ -69,23 +149,6 @@ public class NamespaceService { ...@@ -69,23 +149,6 @@ public class NamespaceService {
return namespaceRepository.save(namespace); 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 @Transactional
public Namespace save(Namespace entity) { public Namespace save(Namespace entity) {
if (!isNamespaceUnique(entity.getAppId(), entity.getClusterName(), entity.getNamespaceName())) { if (!isNamespaceUnique(entity.getAppId(), entity.getClusterName(), entity.getNamespaceName())) {
...@@ -131,4 +194,6 @@ public class NamespaceService { ...@@ -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; 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.biz.entity.Namespace;
import com.ctrip.framework.apollo.core.utils.ByteUtil; import com.ctrip.framework.apollo.common.utils.UniqueKeyGenerator;
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;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
public class ReleaseKeyGenerator { public class ReleaseKeyGenerator extends UniqueKeyGenerator {
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("-");
/** /**
* Generate the release key in the format: timestamp+appId+cluster+namespace+hash(ipAsInt+counter) * Generate the release key in the format: timestamp+appId+cluster+namespace+hash(ipAsInt+counter)
...@@ -30,29 +17,6 @@ public class ReleaseKeyGenerator { ...@@ -30,29 +17,6 @@ public class ReleaseKeyGenerator {
* @return the unique release key * @return the unique release key
*/ */
public static String generateReleaseKey(Namespace namespace) { public static String generateReleaseKey(Namespace namespace) {
String hexIdString = return generate(namespace.getAppId(), namespace.getClusterName(), namespace.getNamespaceName());
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;
} }
} }
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; package com.ctrip.framework.apollo.biz;
import com.ctrip.framework.apollo.biz.eureka.ApolloEurekaClientConfigTest; 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.DatabaseMessageSenderTest;
import com.ctrip.framework.apollo.biz.message.ReleaseMessageScannerTest; import com.ctrip.framework.apollo.biz.message.ReleaseMessageScannerTest;
import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepositoryTest; import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepositoryTest;
...@@ -9,7 +10,9 @@ import com.ctrip.framework.apollo.biz.service.AdminServiceTest; ...@@ -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.AdminServiceTransactionTest;
import com.ctrip.framework.apollo.biz.service.ClusterServiceTest; import com.ctrip.framework.apollo.biz.service.ClusterServiceTest;
import com.ctrip.framework.apollo.biz.service.InstanceServiceTest; 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.PrivilegeServiceTest;
import com.ctrip.framework.apollo.biz.service.ReleaseCreationTest;
import com.ctrip.framework.apollo.biz.service.ReleaseServiceTest; import com.ctrip.framework.apollo.biz.service.ReleaseServiceTest;
import com.ctrip.framework.apollo.biz.service.ServerConfigServiceTest; import com.ctrip.framework.apollo.biz.service.ServerConfigServiceTest;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGeneratorTest; import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGeneratorTest;
...@@ -32,7 +35,10 @@ import org.junit.runners.Suite.SuiteClasses; ...@@ -32,7 +35,10 @@ import org.junit.runners.Suite.SuiteClasses;
ReleaseMessageScannerTest.class, ReleaseMessageScannerTest.class,
ClusterServiceTest.class, ClusterServiceTest.class,
ReleaseKeyGeneratorTest.class, ReleaseKeyGeneratorTest.class,
InstanceServiceTest.class InstanceServiceTest.class,
GrayReleaseRulesHolderTest.class,
NamespaceBranchServiceTest.class,
ReleaseCreationTest.class
}) })
public class AllTests { 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{ ...@@ -28,7 +28,7 @@ public class AdminServiceTest extends AbstractIntegrationTest{
private AppRepository appRepository; private AppRepository appRepository;
@Autowired @Autowired
private ClusterService clsuterService; private ClusterService clusterService;
@Autowired @Autowired
private NamespaceService namespaceService; private NamespaceService namespaceService;
...@@ -49,7 +49,7 @@ public class AdminServiceTest extends AbstractIntegrationTest{ ...@@ -49,7 +49,7 @@ public class AdminServiceTest extends AbstractIntegrationTest{
app = adminService.createNewApp(app); app = adminService.createNewApp(app);
Assert.assertEquals(appId, app.getAppId()); 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(1, clusters.size());
Assert.assertEquals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusters.get(0).getName()); Assert.assertEquals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusters.get(0).getName());
......
...@@ -86,7 +86,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -86,7 +86,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
String anotherReleaseKey = "anotherReleaseKey"; String anotherReleaseKey = "anotherReleaseKey";
InstanceConfig instanceConfig = instanceService.findInstanceConfig(someInstanceId, InstanceConfig instanceConfig = instanceService.findInstanceConfig(someInstanceId,
someConfigAppId, someConfigClusterName, someConfigNamespaceName); someConfigAppId, someConfigNamespaceName);
assertNull(instanceConfig); assertNull(instanceConfig);
...@@ -94,7 +94,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -94,7 +94,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
someConfigClusterName, someConfigNamespaceName, someReleaseKey)); someConfigClusterName, someConfigNamespaceName, someReleaseKey));
instanceConfig = instanceService.findInstanceConfig(someInstanceId, someConfigAppId, instanceConfig = instanceService.findInstanceConfig(someInstanceId, someConfigAppId,
someConfigClusterName, someConfigNamespaceName); someConfigNamespaceName);
assertNotEquals(0, instanceConfig.getId()); assertNotEquals(0, instanceConfig.getId());
assertEquals(someReleaseKey, instanceConfig.getReleaseKey()); assertEquals(someReleaseKey, instanceConfig.getReleaseKey());
...@@ -104,7 +104,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -104,7 +104,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
instanceService.updateInstanceConfig(instanceConfig); instanceService.updateInstanceConfig(instanceConfig);
InstanceConfig updated = instanceService.findInstanceConfig(someInstanceId, someConfigAppId, InstanceConfig updated = instanceService.findInstanceConfig(someInstanceId, someConfigAppId,
someConfigClusterName, someConfigNamespaceName); someConfigNamespaceName);
assertEquals(instanceConfig.getId(), updated.getId()); assertEquals(instanceConfig.getId(), updated.getId());
assertEquals(anotherReleaseKey, updated.getReleaseKey()); assertEquals(anotherReleaseKey, updated.getReleaseKey());
...@@ -170,6 +170,42 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -170,6 +170,42 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
assertEquals(Lists.newArrayList(someInstance, anotherInstance), result.getContent()); 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 @Test
@Rollback @Rollback
public void testFindInstanceConfigsByNamespaceWithReleaseKeysNotIn() throws Exception { 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 { ...@@ -40,7 +40,7 @@ public class PrivilegeServiceTest extends AbstractIntegrationTest {
app.setDataChangeCreatedTime(new Date()); app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app); App newApp = adminService.createNewApp(app);
List<Cluster> clusters = clusterService.findClusters(newApp.getAppId()); List<Cluster> clusters = clusterService.findParentClusters(newApp.getAppId());
List<Namespace> namespaces = List<Namespace> namespaces =
namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName()); namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName());
Namespace namespace = namespaces.get(0); Namespace namespace = namespaces.get(0);
...@@ -70,7 +70,7 @@ public class PrivilegeServiceTest extends AbstractIntegrationTest { ...@@ -70,7 +70,7 @@ public class PrivilegeServiceTest extends AbstractIntegrationTest {
app.setDataChangeLastModifiedBy(owner); app.setDataChangeLastModifiedBy(owner);
app.setDataChangeCreatedTime(new Date()); app.setDataChangeCreatedTime(new Date());
App newApp = adminService.createNewApp(app); App newApp = adminService.createNewApp(app);
List<Cluster> clusters = clusterService.findClusters(newApp.getAppId()); List<Cluster> clusters = clusterService.findParentClusters(newApp.getAppId());
List<Namespace> namespaces = List<Namespace> namespaces =
namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName()); namespaceService.findNamespaces(newApp.getAppId(), clusters.get(0).getName());
Namespace namespace = namespaces.get(0); Namespace namespace = namespaces.get(0);
......
...@@ -32,6 +32,10 @@ public class ReleaseServiceTest extends AbstractUnitTest { ...@@ -32,6 +32,10 @@ public class ReleaseServiceTest extends AbstractUnitTest {
private ReleaseRepository releaseRepository; private ReleaseRepository releaseRepository;
@Mock @Mock
private NamespaceService namespaceService; private NamespaceService namespaceService;
@Mock
private ReleaseHistoryService releaseHistoryService;
@Mock
private ItemSetService itemSetService;
@InjectMocks @InjectMocks
private ReleaseService releaseService; private ReleaseService releaseService;
......
INSERT INTO AppNamespace (AppId, Name, IsPublic) VALUES ('100003171', 'application', false); 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 ('100003171', 'fx.apollo.config', true);
INSERT INTO AppNamespace (AppId, Name, IsPublic) VALUES ('100003172', 'application', false); 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; 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.LogEnabled;
import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.logging.Logger;
import org.unidal.dal.jdbc.datasource.DataSourceProvider; import org.unidal.dal.jdbc.datasource.DataSourceProvider;
...@@ -7,11 +12,6 @@ import org.unidal.dal.jdbc.datasource.model.entity.DataSourcesDef; ...@@ -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.dal.jdbc.datasource.model.transform.DefaultSaxParser;
import org.unidal.lookup.annotation.Named; 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. * Data source provider based on Apollo configuration service.
* <p> * <p>
......
package com.ctrip.framework.apollo.model; package com.ctrip.framework.apollo.model;
import com.google.common.base.Objects;
import com.ctrip.framework.apollo.enums.PropertyChangeType; import com.ctrip.framework.apollo.enums.PropertyChangeType;
/** /**
......
...@@ -47,7 +47,7 @@ public class ConfigUtil { ...@@ -47,7 +47,7 @@ public class ConfigUtil {
String appId = Foundation.app().getAppId(); String appId = Foundation.app().getAppId();
if (Strings.isNullOrEmpty(appId)) { if (Strings.isNullOrEmpty(appId)) {
appId = ConfigConsts.NO_APPID_PLACEHOLDER; 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; 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; ...@@ -3,12 +3,10 @@ package com.ctrip.framework.apollo.common.controller;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import org.springframework.boot.context.embedded.FilterRegistrationBean; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.dianping.cat.servlet.CatFilter; import com.dianping.cat.servlet.CatFilter;
import com.dianping.cat.servlet.CatListener;
@Configuration @Configuration
public class CatConfig { public class CatConfig {
...@@ -23,11 +21,4 @@ public class CatConfig { ...@@ -23,11 +21,4 @@ public class CatConfig {
return bean; 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{ ...@@ -8,6 +8,8 @@ public class ClusterDTO extends BaseDTO{
private String appId; private String appId;
private long parentClusterId;
public long getId() { public long getId() {
return id; return id;
} }
...@@ -31,4 +33,12 @@ public class ClusterDTO extends BaseDTO{ ...@@ -31,4 +33,12 @@ public class ClusterDTO extends BaseDTO{
public void setAppId(String appId) { public void setAppId(String appId) {
this.appId = 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> { ...@@ -37,4 +37,8 @@ public class PageDTO<T> {
public int getSize() { public int getSize() {
return size; 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; 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.biz.message.ReleaseMessageScanner;
import com.ctrip.framework.apollo.configservice.controller.ConfigFileController; import com.ctrip.framework.apollo.configservice.controller.ConfigFileController;
import com.ctrip.framework.apollo.configservice.controller.NotificationController; import com.ctrip.framework.apollo.configservice.controller.NotificationController;
...@@ -14,21 +15,34 @@ import org.springframework.context.annotation.Configuration; ...@@ -14,21 +15,34 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration @Configuration
public class ConfigServiceAutoConfiguration { public class ConfigServiceAutoConfiguration {
@Bean
public GrayReleaseRulesHolder grayReleaseRulesHolder() {
return new GrayReleaseRulesHolder();
}
@Configuration
static class MessageScannerConfiguration {
@Autowired @Autowired
private NotificationController notificationController; private NotificationController notificationController;
@Autowired @Autowired
private ConfigFileController configFileController; private ConfigFileController configFileController;
@Autowired @Autowired
private NotificationControllerV2 notificationControllerV2; private NotificationControllerV2 notificationControllerV2;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
@Bean @Bean
public ReleaseMessageScanner releaseMessageScanner() { public ReleaseMessageScanner releaseMessageScanner() {
ReleaseMessageScanner releaseMessageScanner = new ReleaseMessageScanner(); ReleaseMessageScanner releaseMessageScanner = new ReleaseMessageScanner();
//handle server cache first //1. handle gray release rule
releaseMessageScanner.addMessageListener(grayReleaseRulesHolder);
//2. handle server cache
releaseMessageScanner.addMessageListener(configFileController); releaseMessageScanner.addMessageListener(configFileController);
//3. notify clients
releaseMessageScanner.addMessageListener(notificationControllerV2); releaseMessageScanner.addMessageListener(notificationControllerV2);
releaseMessageScanner.addMessageListener(notificationController); releaseMessageScanner.addMessageListener(notificationController);
return releaseMessageScanner; return releaseMessageScanner;
} }
}
} }
...@@ -10,6 +10,7 @@ import com.google.gson.Gson; ...@@ -10,6 +10,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.biz.entity.Release; 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.AppNamespaceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService; import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
...@@ -51,6 +52,8 @@ public class ConfigController { ...@@ -51,6 +52,8 @@ public class ConfigController {
private NamespaceUtil namespaceUtil; private NamespaceUtil namespaceUtil;
@Autowired @Autowired
private InstanceConfigAuditUtil instanceConfigAuditUtil; private InstanceConfigAuditUtil instanceConfigAuditUtil;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
private static final Type configurationTypeReference = private static final Type configurationTypeReference =
...@@ -80,7 +83,8 @@ public class ConfigController { ...@@ -80,7 +83,8 @@ public class ConfigController {
String appClusterNameLoaded = clusterName; String appClusterNameLoaded = clusterName;
if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) { 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) { if (currentAppRelease != null) {
releases.add(currentAppRelease); releases.add(currentAppRelease);
...@@ -91,7 +95,8 @@ public class ConfigController { ...@@ -91,7 +95,8 @@ public class ConfigController {
//if namespace does not belong to this appId, should check if there is a public configuration //if namespace does not belong to this appId, should check if there is a public configuration
if (!namespaceBelongsToAppId(appId, namespace)) { 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)) { if (!Objects.isNull(publicRelease)) {
releases.add(publicRelease); releases.add(publicRelease);
} }
...@@ -146,30 +151,31 @@ public class ConfigController { ...@@ -146,30 +151,31 @@ public class ConfigController {
} }
/** /**
* @param applicationId the application which uses public config * @param clientAppId the application which uses public config
* @param namespace the namespace * @param namespace the namespace
* @param dataCenter the datacenter * @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) { String dataCenter) {
AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespace); AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespace);
//check whether the namespace's appId equals to current one //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; return null;
} }
String publicConfigAppId = appNamespace.getAppId(); 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 private Release loadConfig(String clientAppId, String clientIp, String configAppId, String
dataCenter) { configClusterName, String configNamespace, String dataCenter) {
//load from specified cluster fist //load from specified cluster fist
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusterName)) { if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, configClusterName)) {
Release clusterRelease = Release clusterRelease = findRelease(clientAppId, clientIp, configAppId, configClusterName,
releaseService.findLatestActiveRelease(appId, clusterName, namespace); configNamespace);
if (!Objects.isNull(clusterRelease)) { if (!Objects.isNull(clusterRelease)) {
return clusterRelease; return clusterRelease;
...@@ -177,17 +183,36 @@ public class ConfigController { ...@@ -177,17 +183,36 @@ public class ConfigController {
} }
//try to load via data center //try to load via data center
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, clusterName)) { if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, configClusterName)) {
Release dataCenterRelease = Release dataCenterRelease = findRelease(clientAppId, clientIp, configAppId, dataCenter,
releaseService.findLatestActiveRelease(appId, dataCenter, namespace); configNamespace);
if (!Objects.isNull(dataCenterRelease)) { if (!Objects.isNull(dataCenterRelease)) {
return dataCenterRelease; return dataCenterRelease;
} }
} }
//fallback to default release //fallback to default release
return releaseService return findRelease(clientAppId, clientIp, configAppId, ConfigConsts.CLUSTER_NAME_DEFAULT,
.findLatestActiveRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace); 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 { ...@@ -217,7 +242,8 @@ public class ConfigController {
return; return;
} }
for (Release release : releases) { 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()); release.getNamespaceName(), release.getReleaseKey());
} }
} }
......
package com.ctrip.framework.apollo.configservice.controller; package com.ctrip.framework.apollo.configservice.controller;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
...@@ -14,6 +15,7 @@ import com.google.common.collect.Multimaps; ...@@ -14,6 +15,7 @@ import com.google.common.collect.Multimaps;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage; 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.ReleaseMessageListener;
import com.ctrip.framework.apollo.biz.message.Topics; import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil; import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
...@@ -53,6 +55,8 @@ import javax.servlet.http.HttpServletResponse; ...@@ -53,6 +55,8 @@ import javax.servlet.http.HttpServletResponse;
public class ConfigFileController implements ReleaseMessageListener { public class ConfigFileController implements ReleaseMessageListener {
private static final Logger logger = LoggerFactory.getLogger(ConfigFileController.class); private static final Logger logger = LoggerFactory.getLogger(ConfigFileController.class);
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR); 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 MAX_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
private static final long EXPIRE_AFTER_WRITE = 30; private static final long EXPIRE_AFTER_WRITE = 30;
private final HttpHeaders propertiesResponseHeaders; private final HttpHeaders propertiesResponseHeaders;
...@@ -74,6 +78,9 @@ public class ConfigFileController implements ReleaseMessageListener { ...@@ -74,6 +78,9 @@ public class ConfigFileController implements ReleaseMessageListener {
@Autowired @Autowired
private WatchKeysUtil watchKeysUtil; private WatchKeysUtil watchKeysUtil;
@Autowired
private GrayReleaseRulesHolder grayReleaseRulesHolder;
public ConfigFileController() { public ConfigFileController() {
localCache = CacheBuilder.newBuilder() localCache = CacheBuilder.newBuilder()
.expireAfterWrite(EXPIRE_AFTER_WRITE, TimeUnit.MINUTES) .expireAfterWrite(EXPIRE_AFTER_WRITE, TimeUnit.MINUTES)
...@@ -157,31 +164,41 @@ public class ConfigFileController implements ReleaseMessageListener { ...@@ -157,31 +164,41 @@ public class ConfigFileController implements ReleaseMessageListener {
//strip out .properties suffix //strip out .properties suffix
namespace = namespaceUtil.filterNamespaceName(namespace); 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); 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); String result = localCache.getIfPresent(cacheKey);
//4. if not exists, load from ConfigController
if (Strings.isNullOrEmpty(result)) { if (Strings.isNullOrEmpty(result)) {
Cat.logEvent("ConfigFile.Cache.Miss", cacheKey); Cat.logEvent("ConfigFile.Cache.Miss", cacheKey);
ApolloConfig apolloConfig = result = loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp,
configController request, response);
.queryConfig(appId, clusterName, namespace, dataCenter, "-1", clientIp, request,
response);
if (apolloConfig == null || apolloConfig.getConfigurations() == null) { if (result == null) {
return null; return null;
} }
//5. Double check if this client needs to load gray release, if yes, load from db again
switch (outputFormat) { //This step is mainly to avoid cache pollution
case PROPERTIES: if (grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, namespace)) {
Properties properties = new Properties(); Cat.logEvent("ConfigFile.Cache.GrayReleaseConflict", cacheKey);
properties.putAll(apolloConfig.getConfigurations()); return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp,
result = PropertiesUtil.toString(properties); request, response);
break;
case JSON:
result = gson.toJson(apolloConfig.getConfigurations());
break;
} }
localCache.put(cacheKey, result); localCache.put(cacheKey, result);
...@@ -203,6 +220,33 @@ public class ConfigFileController implements ReleaseMessageListener { ...@@ -203,6 +220,33 @@ public class ConfigFileController implements ReleaseMessageListener {
return result; 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 assembleCacheKey(ConfigFileOutputFormat outputFormat, String appId, String clusterName,
String namespace, String namespace,
String dataCenter) { String dataCenter) {
...@@ -249,4 +293,12 @@ public class ConfigFileController implements ReleaseMessageListener { ...@@ -249,4 +293,12 @@ public class ConfigFileController implements ReleaseMessageListener {
return value; 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 { ...@@ -74,7 +74,7 @@ public class InstanceConfigAuditUtil implements InitializingBean {
//load instance config release key from cache, and check if release key is the same //load instance config release key from cache, and check if release key is the same
String instanceConfigCacheKey = assembleInstanceConfigKey(instanceId, auditModel String instanceConfigCacheKey = assembleInstanceConfigKey(instanceId, auditModel
.getConfigAppId(), auditModel.getConfigClusterName(), auditModel.getConfigNamespace()); .getConfigAppId(), auditModel.getConfigNamespace());
String cacheReleaseKey = instanceConfigReleaseKeyCache.getIfPresent(instanceConfigCacheKey); String cacheReleaseKey = instanceConfigReleaseKeyCache.getIfPresent(instanceConfigCacheKey);
//if release key is the same, then skip audit //if release key is the same, then skip audit
...@@ -86,10 +86,11 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -86,10 +86,11 @@ public class InstanceConfigAuditUtil implements InitializingBean {
//if release key is not the same or cannot find in cache, then do audit //if release key is not the same or cannot find in cache, then do audit
InstanceConfig instanceConfig = instanceService.findInstanceConfig(instanceId, auditModel InstanceConfig instanceConfig = instanceService.findInstanceConfig(instanceId, auditModel
.getConfigAppId(), auditModel.getConfigClusterName(), auditModel.getConfigNamespace()); .getConfigAppId(), auditModel.getConfigNamespace());
if (instanceConfig != null) { if (instanceConfig != null) {
if (!Objects.equals(instanceConfig.getReleaseKey(), auditModel.getReleaseKey())) { if (!Objects.equals(instanceConfig.getReleaseKey(), auditModel.getReleaseKey())) {
instanceConfig.setConfigClusterName(auditModel.getConfigClusterName());
instanceConfig.setReleaseKey(auditModel.getReleaseKey()); instanceConfig.setReleaseKey(auditModel.getReleaseKey());
instanceConfig.setReleaseDeliveryTime(auditModel.getOfferTime()); instanceConfig.setReleaseDeliveryTime(auditModel.getOfferTime());
} }
...@@ -164,10 +165,8 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -164,10 +165,8 @@ public class InstanceConfigAuditUtil implements InitializingBean {
return STRING_JOINER.join(keyParts); return STRING_JOINER.join(keyParts);
} }
private String assembleInstanceConfigKey(long instanceId, String configAppId, String private String assembleInstanceConfigKey(long instanceId, String configAppId, String configNamespace) {
configClusterName, return STRING_JOINER.join(instanceId, configAppId, configNamespace);
String configNamespace) {
return STRING_JOINER.join(instanceId, configAppId, configClusterName, configNamespace);
} }
public static class InstanceConfigAuditModel { public static class InstanceConfigAuditModel {
......
...@@ -6,6 +6,7 @@ import com.google.common.collect.Lists; ...@@ -6,6 +6,7 @@ import com.google.common.collect.Lists;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException; 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.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
...@@ -63,6 +64,8 @@ public class ConfigControllerTest { ...@@ -63,6 +64,8 @@ public class ConfigControllerTest {
@Mock @Mock
private InstanceConfigAuditUtil instanceConfigAuditUtil; private InstanceConfigAuditUtil instanceConfigAuditUtil;
@Mock @Mock
private GrayReleaseRulesHolder grayReleaseRulesHolder;
@Mock
private HttpServletRequest someRequest; private HttpServletRequest someRequest;
@Before @Before
...@@ -72,6 +75,7 @@ public class ConfigControllerTest { ...@@ -72,6 +75,7 @@ public class ConfigControllerTest {
ReflectionTestUtils.setField(configController, "appNamespaceService", appNamespaceService); ReflectionTestUtils.setField(configController, "appNamespaceService", appNamespaceService);
ReflectionTestUtils.setField(configController, "namespaceUtil", namespaceUtil); ReflectionTestUtils.setField(configController, "namespaceUtil", namespaceUtil);
ReflectionTestUtils.setField(configController, "instanceConfigAuditUtil", instanceConfigAuditUtil); ReflectionTestUtils.setField(configController, "instanceConfigAuditUtil", instanceConfigAuditUtil);
ReflectionTestUtils.setField(configController, "grayReleaseRulesHolder", grayReleaseRulesHolder);
someAppId = "1"; someAppId = "1";
someClusterName = "someClusterName"; someClusterName = "someClusterName";
...@@ -90,6 +94,8 @@ public class ConfigControllerTest { ...@@ -90,6 +94,8 @@ public class ConfigControllerTest {
when(namespaceUtil.filterNamespaceName(defaultNamespaceName)).thenReturn(defaultNamespaceName); when(namespaceUtil.filterNamespaceName(defaultNamespaceName)).thenReturn(defaultNamespaceName);
when(namespaceUtil.filterNamespaceName(somePublicNamespaceName)) when(namespaceUtil.filterNamespaceName(somePublicNamespaceName))
.thenReturn(somePublicNamespaceName); .thenReturn(somePublicNamespaceName);
when(grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(anyString(), anyString(),
anyString(), anyString(), anyString())).thenReturn(null);
} }
@Test @Test
...@@ -116,6 +122,45 @@ public class ConfigControllerTest { ...@@ -116,6 +122,45 @@ public class ConfigControllerTest {
someClientIp, someAppId, someClusterName, defaultNamespaceName, someServerSideNewReleaseKey); 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 @Test
public void testQueryConfigFile() throws Exception { public void testQueryConfigFile() throws Exception {
String someClientSideReleaseKey = "1"; String someClientSideReleaseKey = "1";
......
...@@ -9,6 +9,7 @@ import com.google.common.reflect.TypeToken; ...@@ -9,6 +9,7 @@ import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage; 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.biz.message.Topics;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil; import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.configservice.util.WatchKeysUtil; import com.ctrip.framework.apollo.configservice.util.WatchKeysUtil;
...@@ -33,6 +34,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -33,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
...@@ -49,6 +51,8 @@ public class ConfigFileControllerTest { ...@@ -49,6 +51,8 @@ public class ConfigFileControllerTest {
private WatchKeysUtil watchKeysUtil; private WatchKeysUtil watchKeysUtil;
@Mock @Mock
private NamespaceUtil namespaceUtil; private NamespaceUtil namespaceUtil;
@Mock
private GrayReleaseRulesHolder grayReleaseRulesHolder;
private ConfigFileController configFileController; private ConfigFileController configFileController;
private String someAppId; private String someAppId;
private String someClusterName; private String someClusterName;
...@@ -68,6 +72,7 @@ public class ConfigFileControllerTest { ...@@ -68,6 +72,7 @@ public class ConfigFileControllerTest {
ReflectionTestUtils.setField(configFileController, "configController", configController); ReflectionTestUtils.setField(configFileController, "configController", configController);
ReflectionTestUtils.setField(configFileController, "watchKeysUtil", watchKeysUtil); ReflectionTestUtils.setField(configFileController, "watchKeysUtil", watchKeysUtil);
ReflectionTestUtils.setField(configFileController, "namespaceUtil", namespaceUtil); ReflectionTestUtils.setField(configFileController, "namespaceUtil", namespaceUtil);
ReflectionTestUtils.setField(configFileController, "grayReleaseRulesHolder", grayReleaseRulesHolder);
someAppId = "someAppId"; someAppId = "someAppId";
someClusterName = "someClusterName"; someClusterName = "someClusterName";
...@@ -76,6 +81,8 @@ public class ConfigFileControllerTest { ...@@ -76,6 +81,8 @@ public class ConfigFileControllerTest {
someClientIp = "10.1.1.1"; someClientIp = "10.1.1.1";
when(namespaceUtil.filterNamespaceName(someNamespace)).thenReturn(someNamespace); when(namespaceUtil.filterNamespaceName(someNamespace)).thenReturn(someNamespace);
when(grayReleaseRulesHolder.hasGrayReleaseRule(anyString(), anyString(), anyString()))
.thenReturn(false);
watchedKeys2CacheKey = watchedKeys2CacheKey =
(Multimap<String, String>) ReflectionTestUtils (Multimap<String, String>) ReflectionTestUtils
...@@ -169,6 +176,45 @@ public class ConfigFileControllerTest { ...@@ -169,6 +176,45 @@ public class ConfigFileControllerTest {
assertEquals(configurations, gson.fromJson(response.getBody(), responseType)); 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 @Test
public void testHandleMessage() throws Exception { public void testHandleMessage() throws Exception {
String someWatchKey = "someWatchKey"; String someWatchKey = "someWatchKey";
......
package com.ctrip.framework.apollo.configservice.integration; 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.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig; import com.ctrip.framework.apollo.core.dto.ApolloConfig;
...@@ -10,6 +12,11 @@ import org.springframework.http.ResponseEntity; ...@@ -10,6 +12,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql;
import org.springframework.web.client.HttpStatusCodeException; 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; import static org.junit.Assert.assertEquals;
/** /**
...@@ -17,20 +24,26 @@ import static org.junit.Assert.assertEquals; ...@@ -17,20 +24,26 @@ import static org.junit.Assert.assertEquals;
*/ */
public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest { public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest {
private String someAppId; private String someAppId;
private String somePublicAppId;
private String someCluster; private String someCluster;
private String someNamespace; private String someNamespace;
private String somePublicNamespace; private String somePublicNamespace;
private String someDC; private String someDC;
private String someDefaultCluster; private String someDefaultCluster;
private String someClientIp;
private ExecutorService executorService;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
someAppId = "someAppId"; someAppId = "someAppId";
someCluster = "someCluster"; someCluster = "someCluster";
someNamespace = "someNamespace"; someNamespace = "someNamespace";
somePublicAppId = "somePublicAppId";
somePublicNamespace = "somePublicNamespace"; somePublicNamespace = "somePublicNamespace";
someDC = "someDC"; someDC = "someDC";
someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT; someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
someClientIp = "1.1.1.1";
executorService = Executors.newSingleThreadExecutor();
} }
@Test @Test
...@@ -47,6 +60,29 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest ...@@ -47,6 +60,29 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("v1", result.getConfigurations().get("k1")); 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 @Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @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) @Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
...@@ -119,6 +155,30 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest ...@@ -119,6 +155,30 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals(HttpStatus.NOT_MODIFIED, response.getStatusCode()); 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 @Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @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) @Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
...@@ -199,6 +259,33 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest ...@@ -199,6 +259,33 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("default-v2", result.getConfigurations().get("k2")); 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 @Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @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) @Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
...@@ -250,4 +337,8 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest ...@@ -250,4 +337,8 @@ public class ConfigControllerIntegrationTest extends AbstractBaseIntegrationTest
assertEquals("someDC-v1", result.getConfigurations().get("k1")); assertEquals("someDC-v1", result.getConfigurations().get("k1"));
assertEquals("someDC-v2", result.getConfigurations().get("k2")); 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; package com.ctrip.framework.apollo.configservice.integration;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
...@@ -18,9 +19,13 @@ import org.springframework.test.context.jdbc.Sql; ...@@ -18,9 +19,13 @@ import org.springframework.test.context.jdbc.Sql;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
...@@ -28,22 +33,30 @@ import static org.junit.Assert.assertTrue; ...@@ -28,22 +33,30 @@ import static org.junit.Assert.assertTrue;
*/ */
public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegrationTest { public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegrationTest {
private String someAppId; private String someAppId;
private String somePublicAppId;
private String someCluster; private String someCluster;
private String someNamespace; private String someNamespace;
private String somePublicNamespace; private String somePublicNamespace;
private String someDC; private String someDC;
private String someDefaultCluster; private String someDefaultCluster;
private String grayClientIp;
private String nonGrayClientIp;
private Gson gson = new Gson(); private Gson gson = new Gson();
private ExecutorService executorService;
private Type mapResponseType = new TypeToken<Map<String, String>>(){}.getType(); private Type mapResponseType = new TypeToken<Map<String, String>>(){}.getType();
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT; someDefaultCluster = ConfigConsts.CLUSTER_NAME_DEFAULT;
someAppId = "someAppId"; someAppId = "someAppId";
somePublicAppId = "somePublicAppId";
someCluster = "someCluster"; someCluster = "someCluster";
someNamespace = "someNamespace"; someNamespace = "someNamespace";
somePublicNamespace = "somePublicNamespace"; somePublicNamespace = "somePublicNamespace";
someDC = "someDC"; someDC = "someDC";
grayClientIp = "1.1.1.1";
nonGrayClientIp = "2.2.2.2";
executorService = Executors.newSingleThreadExecutor();
} }
@Test @Test
...@@ -61,6 +74,40 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration ...@@ -61,6 +74,40 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertTrue(result.contains("k2=v2")); 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 @Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @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) @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 ...@@ -89,7 +136,6 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
.getForEntity("{baseurl}/configfiles/json/{appId}/{clusterName}/{namespace}", String.class, .getForEntity("{baseurl}/configfiles/json/{appId}/{clusterName}/{namespace}", String.class,
getHostUrl(), someAppId, someCluster, someNamespace); getHostUrl(), someAppId, someCluster, someNamespace);
String result = response.getBody();
Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType); Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType);
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
...@@ -108,7 +154,6 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration ...@@ -108,7 +154,6 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
String.class, String.class,
getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, someDC); getHostUrl(), someAppId, someDefaultCluster, somePublicNamespace, someDC);
String result = response.getBody();
Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType); Map<String, String> configs = gson.fromJson(response.getBody(), mapResponseType);
assertEquals(HttpStatus.OK, response.getStatusCode()); assertEquals(HttpStatus.OK, response.getStatusCode());
...@@ -116,6 +161,47 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration ...@@ -116,6 +161,47 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertEquals("someDC-v2", configs.get("k2")); 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 @Test
@Sql(scripts = "/integration-test/test-release.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @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) @Sql(scripts = "/integration-test/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
...@@ -166,4 +252,7 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration ...@@ -166,4 +252,7 @@ public class ConfigFileControllerIntegrationTest extends AbstractBaseIntegration
assertTrue(result.contains("k2=v2-changed")); 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; ...@@ -13,12 +13,9 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.BlockingQueue; 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.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
...@@ -60,13 +57,13 @@ public class InstanceConfigAuditUtilTest { ...@@ -60,13 +57,13 @@ public class InstanceConfigAuditUtilTest {
someDataCenter = "someDataCenter"; someDataCenter = "someDataCenter";
someIp = "someIp"; someIp = "someIp";
someConfigAppId = "someConfigAppId"; someConfigAppId = "someConfigAppId";
someConfigClusterName= "someConfigClusterName"; someConfigClusterName = "someConfigClusterName";
someConfigNamespace = "someConfigNamespace"; someConfigNamespace = "someConfigNamespace";
someReleaseKey = "someReleaseKey"; someReleaseKey = "someReleaseKey";
someAuditModel = new InstanceConfigAuditUtil.InstanceConfigAuditModel(someAppId, someAuditModel = new InstanceConfigAuditUtil.InstanceConfigAuditModel(someAppId,
someClusterName, someDataCenter, someIp, someConfigAppId, someConfigClusterName, someConfigNamespace, someClusterName, someDataCenter, someIp, someConfigAppId, someConfigClusterName,
someReleaseKey); someConfigNamespace, someReleaseKey);
} }
@Test @Test
...@@ -93,7 +90,7 @@ public class InstanceConfigAuditUtilTest { ...@@ -93,7 +90,7 @@ public class InstanceConfigAuditUtilTest {
verify(instanceService, times(1)).findInstance(someAppId, someClusterName, someDataCenter, verify(instanceService, times(1)).findInstance(someAppId, someClusterName, someDataCenter,
someIp); someIp);
verify(instanceService, times(1)).createInstance(any(Instance.class)); verify(instanceService, times(1)).createInstance(any(Instance.class));
verify(instanceService, times(1)).findInstanceConfig(someInstanceId, someConfigAppId, someConfigClusterName, verify(instanceService, times(1)).findInstanceConfig(someInstanceId, someConfigAppId,
someConfigNamespace); someConfigNamespace);
verify(instanceService, times(1)).createInstanceConfig(any(InstanceConfig.class)); verify(instanceService, times(1)).createInstanceConfig(any(InstanceConfig.class));
} }
......
...@@ -4,4 +4,6 @@ DELETE FROM AppNamespace; ...@@ -4,4 +4,6 @@ DELETE FROM AppNamespace;
DELETE FROM Cluster; DELETE FROM Cluster;
DELETE FROM App; DELETE FROM App;
DELETE FROM ReleaseMessage; 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 ...@@ -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"}'); 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) 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"}'); 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; ...@@ -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.core.utils.StringUtils;
import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO; import com.ctrip.framework.apollo.openapi.dto.OpenReleaseDTO;
import com.ctrip.framework.apollo.openapi.util.OpenApiBeanUtils; 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.ReleaseService;
import com.ctrip.framework.apollo.portal.service.UserService; import com.ctrip.framework.apollo.portal.service.UserService;
...@@ -55,7 +55,7 @@ public class ReleaseController { ...@@ -55,7 +55,7 @@ public class ReleaseController {
model.setClusterName(clusterName); model.setClusterName(clusterName);
model.setNamespaceName(namespaceName); 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) @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest", method = RequestMethod.GET)
......
...@@ -139,7 +139,14 @@ public class RetryableRestTemplate { ...@@ -139,7 +139,14 @@ public class RetryableRestTemplate {
} catch (Throwable t) { } catch (Throwable t) {
logger.error("Http request failed, uri: {}, method: {}", uri, HttpMethod.GET, t); logger.error("Http request failed, uri: {}, method: {}", uri, HttpMethod.GET, t);
Cat.logError(t); Cat.logError(t);
if (canRetry(t, HttpMethod.GET)){
Cat.logEvent(CatEventType.API_RETRY, uri); Cat.logEvent(CatEventType.API_RETRY, uri);
}else {// biz exception rethrow
ct.setStatus(t);
ct.complete();
throw t;
}
} }
} }
......
package com.ctrip.framework.apollo.portal.components;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Component
public class ItemsComparator {
public ItemChangeSets compareIgnoreBlankAndCommentItem(long baseNamespaceId, List<ItemDTO> baseItems, List<ItemDTO> targetItems){
List<ItemDTO> filteredSourceItems = filterBlankAndCommentItem(baseItems);
List<ItemDTO> filteredTargetItems = filterBlankAndCommentItem(targetItems);
Map<String, ItemDTO> sourceItemMap = BeanUtils.mapByKey("key", filteredSourceItems);
Map<String, ItemDTO> targetItemMap = BeanUtils.mapByKey("key", filteredTargetItems);
ItemChangeSets changeSets = new ItemChangeSets();
for (ItemDTO item: targetItems){
String key = item.getKey();
ItemDTO sourceItem = sourceItemMap.get(key);
if (sourceItem == null){//add
ItemDTO copiedItem = copyItem(item);
copiedItem.setNamespaceId(baseNamespaceId);
changeSets.addCreateItem(copiedItem);
}else if (!Objects.equals(sourceItem.getValue(), item.getValue())){//update
//only value & comment can be update
sourceItem.setValue(item.getValue());
sourceItem.setComment(item.getComment());
changeSets.addUpdateItem(sourceItem);
}
}
for (ItemDTO item: baseItems){
String key = item.getKey();
ItemDTO targetItem = targetItemMap.get(key);
if(targetItem == null){//delete
changeSets.addDeleteItem(item);
}
}
return changeSets;
}
private List<ItemDTO> filterBlankAndCommentItem(List<ItemDTO> items){
List<ItemDTO> result = new LinkedList<>();
if (CollectionUtils.isEmpty(items)){
return result;
}
for (ItemDTO item: items){
if (!StringUtils.isEmpty(item.getKey())){
result.add(item);
}
}
return result;
}
private ItemDTO copyItem(ItemDTO sourceItem){
ItemDTO copiedItem = new ItemDTO();
copiedItem.setKey(sourceItem.getKey());
copiedItem.setValue(sourceItem.getValue());
copiedItem.setComment(sourceItem.getComment());
return copiedItem;
}
}
...@@ -52,9 +52,9 @@ public class AuthConfiguration { ...@@ -52,9 +52,9 @@ public class AuthConfiguration {
@Bean @Bean
public ServletListenerRegistrationBean redisAppSettingListner() { public ServletListenerRegistrationBean redisAppSettingListner() {
ServletListenerRegistrationBean redisAppSettingListner = new ServletListenerRegistrationBean(); ServletListenerRegistrationBean redisAppSettingListener = new ServletListenerRegistrationBean();
redisAppSettingListner.setListener(listener("org.jasig.cas.client.credis.CRedisAppSettingListner")); redisAppSettingListener.setListener(listener("org.jasig.cas.client.credis.CRedisAppSettingListner"));
return redisAppSettingListner; return redisAppSettingListener;
} }
@Bean @Bean
......
...@@ -20,4 +20,11 @@ public interface CatEventType { ...@@ -20,4 +20,11 @@ public interface CatEventType {
String USER_ACCESS = "User.Access"; 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 { ...@@ -60,7 +60,6 @@ public class AppController {
} }
@RequestMapping("/by-owner") @RequestMapping("/by-owner")
public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page){ public List<App> findAppsByOwner(@RequestParam("owner") String owner, Pageable page){
return appService.findByOwnerName(owner, page); return appService.findByOwnerName(owner, page);
......
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