Commit 6153339b authored by Jason Song's avatar Jason Song Committed by GitHub

Merge pull request #393 from lepdou/instances

Instances
parents 9e27f19d a8af467f
package com.ctrip.framework.apollo.adminservice.controller; package com.ctrip.framework.apollo.adminservice.controller;
import com.google.common.base.Splitter;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.biz.entity.Instance; import com.ctrip.framework.apollo.biz.entity.Instance;
import com.ctrip.framework.apollo.biz.entity.InstanceConfig; import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
...@@ -7,87 +13,146 @@ import com.ctrip.framework.apollo.biz.service.InstanceService; ...@@ -7,87 +13,146 @@ import com.ctrip.framework.apollo.biz.service.InstanceService;
import com.ctrip.framework.apollo.biz.service.ReleaseService; import com.ctrip.framework.apollo.biz.service.ReleaseService;
import com.ctrip.framework.apollo.common.dto.InstanceConfigDTO; import com.ctrip.framework.apollo.common.dto.InstanceConfigDTO;
import com.ctrip.framework.apollo.common.dto.InstanceDTO; import com.ctrip.framework.apollo.common.dto.InstanceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
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 com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; 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.data.domain.Pageable;
import org.springframework.util.CollectionUtils;
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.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RestController @RestController
@RequestMapping(path = "/instances") @RequestMapping("/instances")
public class InstanceConfigController { public class InstanceConfigController {
private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();
@Autowired @Autowired
private ReleaseService releaseService; private ReleaseService releaseService;
@Autowired @Autowired
private InstanceService instanceService; private InstanceService instanceService;
@RequestMapping(value = "/by-release", method = RequestMethod.GET) @RequestMapping(value = "/by-release", method = RequestMethod.GET)
public List<InstanceDTO> getByRelease(@RequestParam("releaseId") long releaseId, public PageDTO<InstanceDTO> getByRelease(@RequestParam("releaseId") long releaseId,
@RequestParam(value = "withReleaseDetail", defaultValue = Pageable pageable) {
"false") boolean withReleaseDetail,
Pageable pageable) {
Release release = releaseService.findOne(releaseId); Release release = releaseService.findOne(releaseId);
if (release == null) { if (release == null) {
throw new NotFoundException(String.format("release not found for %s", releaseId)); throw new NotFoundException(String.format("release not found for %s", releaseId));
} }
List<InstanceConfig> instanceConfigs = instanceService.findActiveInstanceConfigsByReleaseKey Page<InstanceConfig> instanceConfigsPage = instanceService.findActiveInstanceConfigsByReleaseKey
(release.getReleaseKey(), pageable); (release.getReleaseKey(), pageable);
if (instanceConfigs.isEmpty()) { List<InstanceDTO> instanceDTOs = Collections.emptyList();
return Collections.emptyList();
if (instanceConfigsPage.hasContent()) {
Set<Long> instanceIds = instanceConfigsPage.getContent().stream().map
(InstanceConfig::getInstanceId).collect(Collectors.toSet());
List<Instance> instances = instanceService.findInstancesByIds(instanceIds);
if (!CollectionUtils.isEmpty(instances)) {
instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances);
}
}
return new PageDTO<>(instanceDTOs, pageable, instanceConfigsPage.getTotalElements());
}
@RequestMapping(value = "/by-namespace-and-releases-not-in", method = RequestMethod.GET)
public List<InstanceDTO> getByReleasesNotIn(@RequestParam("appId") String appId,
@RequestParam("clusterName") String clusterName,
@RequestParam("namespaceName") String namespaceName,
@RequestParam("releaseIds") String releaseIds) {
Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong)
.collect(Collectors.toSet());
List<Release> releases = releaseService.findByReleaseIds(releaseIdSet);
if (CollectionUtils.isEmpty(releases)) {
throw new NotFoundException(String.format("releases not found for %s", releaseIds));
} }
Map<Long, List<InstanceConfig>> instanceConfigMap = instanceConfigs.stream().collect(Collectors Set<String> releaseKeys = releases.stream().map(Release::getReleaseKey).collect(Collectors
.groupingBy(InstanceConfig::getInstanceId)); .toSet());
List<InstanceConfig> instanceConfigs = instanceService
.findInstanceConfigsByNamespaceWithReleaseKeysNotIn(appId, clusterName, namespaceName,
releaseKeys);
Multimap<Long, InstanceConfig> instanceConfigMap = HashMultimap.create();
Set<String> otherReleaseKeys = Sets.newHashSet();
for (InstanceConfig instanceConfig : instanceConfigs) {
instanceConfigMap.put(instanceConfig.getInstanceId(), instanceConfig);
otherReleaseKeys.add(instanceConfig.getReleaseKey());
}
List<Instance> instances = instanceService.findInstancesByIds(instanceConfigMap.keySet()); List<Instance> instances = instanceService.findInstancesByIds(instanceConfigMap.keySet());
if (instances.isEmpty()) { if (CollectionUtils.isEmpty(instances)) {
return Collections.emptyList(); return Collections.emptyList();
} }
return instances.stream().map(transformToInstanceConfigDto).peek(instanceDTO -> { List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances);
List<InstanceConfig> instanceConfigList = instanceConfigMap.get(instanceDTO.getId());
ReleaseDTO releaseDTO = withReleaseDetail ? BeanUtils.transfrom(ReleaseDTO.class, release) List<Release> otherReleases = releaseService.findByReleaseKeys(otherReleaseKeys);
: null; Map<String, ReleaseDTO> releaseMap = Maps.newHashMap();
instanceDTO.setConfigs(instanceConfigList.stream()
.map(instanceConfig -> transformToInstanceConfigDto(instanceConfig, releaseDTO)) for (Release release : otherReleases) {
.collect(Collectors.toList())); //unset configurations to save space
}).collect(Collectors.toList()); release.setConfigurations(null);
ReleaseDTO releaseDTO = BeanUtils.transfrom(ReleaseDTO.class, release);
releaseMap.put(release.getReleaseKey(), releaseDTO);
}
for (InstanceDTO instanceDTO : instanceDTOs) {
Collection<InstanceConfig> configs = instanceConfigMap.get(instanceDTO.getId());
List<InstanceConfigDTO> configDTOs = configs.stream().map(instanceConfig -> {
InstanceConfigDTO instanceConfigDTO = new InstanceConfigDTO();
instanceConfigDTO.setRelease(releaseMap.get(instanceConfig.getReleaseKey()));
instanceConfigDTO.setDataChangeLastModifiedTime(instanceConfig
.getDataChangeLastModifiedTime());
return instanceConfigDTO;
}).collect(Collectors.toList());
instanceDTO.setConfigs(configDTOs);
}
return instanceDTOs;
} }
private InstanceConfigDTO transformToInstanceConfigDto(InstanceConfig instanceConfig, @RequestMapping(value = "/by-namespace", method = RequestMethod.GET)
ReleaseDTO releaseDTO) { public PageDTO<InstanceDTO> getInstancesByNamespace(@RequestParam("appId") String appId,
InstanceConfigDTO instanceConfigDTO = new InstanceConfigDTO(); @RequestParam("clusterName") String clusterName,
instanceConfigDTO.setDataChangeLastModifiedTime(instanceConfig @RequestParam("namespaceName") String
.getDataChangeLastModifiedTime()); namespaceName, Pageable pageable) {
instanceConfigDTO.setRelease(releaseDTO); Page<Instance> instances = instanceService.findInstancesByNamespace(appId, clusterName,
return instanceConfigDTO; namespaceName, pageable);
List<InstanceDTO> instanceDTOs = BeanUtils.batchTransform(InstanceDTO.class, instances.getContent());
return new PageDTO<>(instanceDTOs, pageable, instances.getTotalElements());
} }
private static Function<Instance, InstanceDTO> transformToInstanceConfigDto = instance -> { @RequestMapping(value = "/by-namespace/count", method = RequestMethod.GET)
InstanceDTO instanceDTO = new InstanceDTO(); public long getInstancesCountByNamespace(@RequestParam("appId") String appId,
instanceDTO.setId(instance.getId()); @RequestParam("clusterName") String clusterName,
instanceDTO.setAppId(instance.getAppId()); @RequestParam("namespaceName") String namespaceName) {
instanceDTO.setClusterName(instance.getClusterName()); Page<Instance> instances = instanceService.findInstancesByNamespace(appId, clusterName,
instanceDTO.setDataCenter(instance.getDataCenter()); namespaceName, new PageRequest(0, 1));
instanceDTO.setIp(instance.getIp()); return instances.getTotalElements();
instanceDTO.setDataChangeCreatedTime(instance.getDataChangeCreatedTime()); }
return instanceDTO;
};
} }
...@@ -29,6 +29,9 @@ public class InstanceConfig { ...@@ -29,6 +29,9 @@ public class InstanceConfig {
@Column(name = "ConfigAppId", nullable = false) @Column(name = "ConfigAppId", nullable = false)
private String configAppId; private String configAppId;
@Column(name = "ConfigClusterName", nullable = false)
private String configClusterName;
@Column(name = "ConfigNamespaceName", nullable = false) @Column(name = "ConfigNamespaceName", nullable = false)
private String configNamespaceName; private String configNamespaceName;
...@@ -112,12 +115,21 @@ public class InstanceConfig { ...@@ -112,12 +115,21 @@ public class InstanceConfig {
this.dataChangeLastModifiedTime = dataChangeLastModifiedTime; this.dataChangeLastModifiedTime = dataChangeLastModifiedTime;
} }
public String getConfigClusterName() {
return configClusterName;
}
public void setConfigClusterName(String configClusterName) {
this.configClusterName = configClusterName;
}
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(this) return MoreObjects.toStringHelper(this)
.omitNullValues() .omitNullValues()
.add("id", id) .add("id", id)
.add("configAppId", configAppId) .add("configAppId", configAppId)
.add("configClusterName", configClusterName)
.add("configNamespaceName", configNamespaceName) .add("configNamespaceName", configNamespaceName)
.add("releaseKey", releaseKey) .add("releaseKey", releaseKey)
.add("dataChangeCreatedTime", dataChangeCreatedTime) .add("dataChangeCreatedTime", dataChangeCreatedTime)
......
...@@ -2,16 +2,26 @@ package com.ctrip.framework.apollo.biz.repository; ...@@ -2,16 +2,26 @@ package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.entity.InstanceConfig; import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set;
public interface InstanceConfigRepository extends PagingAndSortingRepository<InstanceConfig, Long> { public interface InstanceConfigRepository extends PagingAndSortingRepository<InstanceConfig, Long> {
InstanceConfig findByInstanceIdAndConfigAppIdAndConfigNamespaceName(long instanceId, String
configAppId, String configNamespaceName);
List<InstanceConfig> findByReleaseKeyAndDataChangeLastModifiedTimeAfter(String releaseKey, Date InstanceConfig findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName(long instanceId, String
configAppId, String configClusterName, String configNamespaceName);
Page<InstanceConfig> findByReleaseKeyAndDataChangeLastModifiedTimeAfter(String releaseKey, Date
validDate, Pageable pageable); validDate, Pageable pageable);
Page<InstanceConfig> findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfter(
String appId, String clusterName, String namespaceName, Date validDate, Pageable pageable);
List<InstanceConfig> findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfterAndReleaseKeyNotIn(
String appId, String clusterName, String namespaceName, Date validDate, Set<String> releaseKey);
} }
package com.ctrip.framework.apollo.biz.repository; package com.ctrip.framework.apollo.biz.repository;
import java.util.List; import java.util.List;
import java.util.Set;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
...@@ -22,6 +23,8 @@ public interface ReleaseRepository extends PagingAndSortingRepository<Release, L ...@@ -22,6 +23,8 @@ public interface ReleaseRepository extends PagingAndSortingRepository<Release, L
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);
@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);
......
...@@ -9,15 +9,19 @@ import com.ctrip.framework.apollo.biz.repository.InstanceConfigRepository; ...@@ -9,15 +9,19 @@ import com.ctrip.framework.apollo.biz.repository.InstanceConfigRepository;
import com.ctrip.framework.apollo.biz.repository.InstanceRepository; import com.ctrip.framework.apollo.biz.repository.InstanceRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
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 java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -50,20 +54,51 @@ public class InstanceService { ...@@ -50,20 +54,51 @@ public class InstanceService {
return instanceRepository.save(instance); return instanceRepository.save(instance);
} }
public InstanceConfig findInstanceConfig(long instanceId, String configAppId, public InstanceConfig findInstanceConfig(long instanceId, String configAppId, String
String configNamespaceName) { configClusterName, String configNamespaceName) {
return instanceConfigRepository.findByInstanceIdAndConfigAppIdAndConfigNamespaceName( return instanceConfigRepository
instanceId, configAppId, configNamespaceName); .findByInstanceIdAndConfigAppIdAndConfigClusterNameAndConfigNamespaceName(
instanceId, configAppId, configClusterName, configNamespaceName);
} }
public List<InstanceConfig> findActiveInstanceConfigsByReleaseKey(String releaseKey, Pageable public Page<InstanceConfig> findActiveInstanceConfigsByReleaseKey(String releaseKey, Pageable
pageable) { pageable) {
List<InstanceConfig> instanceConfigs = instanceConfigRepository Page<InstanceConfig> instanceConfigs = instanceConfigRepository
.findByReleaseKeyAndDataChangeLastModifiedTimeAfter(releaseKey, .findByReleaseKeyAndDataChangeLastModifiedTimeAfter(releaseKey,
getValidInstanceConfigDate(), pageable); getValidInstanceConfigDate(), pageable);
if (instanceConfigs == null) { return instanceConfigs;
}
public Page<Instance> findInstancesByNamespace(String appId, String clusterName, String
namespaceName, Pageable pageable) {
Page<InstanceConfig> instanceConfigs = instanceConfigRepository.
findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfter(appId, clusterName,
namespaceName, getValidInstanceConfigDate(), pageable);
List<Instance> instances = Collections.emptyList();
if (instanceConfigs.hasContent()) {
Set<Long> instanceIds = instanceConfigs.getContent().stream().map
(InstanceConfig::getInstanceId).collect(Collectors.toSet());
instances = findInstancesByIds(instanceIds);
}
return new PageImpl<>(instances, pageable, instanceConfigs.getTotalElements());
}
public List<InstanceConfig> findInstanceConfigsByNamespaceWithReleaseKeysNotIn(String appId,
String clusterName,
String
namespaceName,
Set<String>
releaseKeysNotIn) {
List<InstanceConfig> instanceConfigs = instanceConfigRepository.
findByConfigAppIdAndConfigClusterNameAndConfigNamespaceNameAndDataChangeLastModifiedTimeAfterAndReleaseKeyNotIn(appId, clusterName,
namespaceName, getValidInstanceConfigDate(), releaseKeysNotIn);
if (CollectionUtils.isEmpty(instanceConfigs)) {
return Collections.emptyList(); return Collections.emptyList();
} }
return instanceConfigs; return instanceConfigs;
} }
......
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import com.google.common.collect.Lists;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.ctrip.framework.apollo.biz.entity.Audit; import com.ctrip.framework.apollo.biz.entity.Audit;
import com.ctrip.framework.apollo.biz.entity.Instance;
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.entity.NamespaceLock; import com.ctrip.framework.apollo.biz.entity.NamespaceLock;
...@@ -14,6 +16,8 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException; ...@@ -14,6 +16,8 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException; import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.apache.commons.collections.collection.UnmodifiableCollection;
import org.apache.commons.collections.list.UnmodifiableList;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
...@@ -25,6 +29,7 @@ import java.util.Date; ...@@ -25,6 +29,7 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
...@@ -48,6 +53,18 @@ public class ReleaseService { ...@@ -48,6 +53,18 @@ public class ReleaseService {
return release; return release;
} }
public List<Release> findByReleaseIds(Set<Long> releaseIds) {
Iterable<Release> releases = releaseRepository.findAll(releaseIds);
if (releases == null) {
return Collections.emptyList();
}
return Lists.newArrayList(releases);
}
public List<Release> findByReleaseKeys(Set<String> releaseKeys) {
return releaseRepository.findByReleaseKeyIn(releaseKeys);
}
public Release findLatestActiveRelease(String appId, String clusterName, String namespaceName) { public Release findLatestActiveRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc( Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
appId, clusterName, namespaceName); appId, clusterName, namespaceName);
......
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
...@@ -8,11 +9,11 @@ import com.ctrip.framework.apollo.biz.entity.InstanceConfig; ...@@ -8,11 +9,11 @@ import com.ctrip.framework.apollo.biz.entity.InstanceConfig;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.test.annotation.Rollback; import org.springframework.test.annotation.Rollback;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
...@@ -79,20 +80,21 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -79,20 +80,21 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
public void testCreateAndFindInstanceConfig() throws Exception { public void testCreateAndFindInstanceConfig() throws Exception {
long someInstanceId = 1; long someInstanceId = 1;
String someConfigAppId = "someConfigAppId"; String someConfigAppId = "someConfigAppId";
String someConfigClusterName = "someConfigClusterName";
String someConfigNamespaceName = "someConfigNamespaceName"; String someConfigNamespaceName = "someConfigNamespaceName";
String someReleaseKey = "someReleaseKey"; String someReleaseKey = "someReleaseKey";
String anotherReleaseKey = "anotherReleaseKey"; String anotherReleaseKey = "anotherReleaseKey";
InstanceConfig instanceConfig = instanceService.findInstanceConfig(someInstanceId, InstanceConfig instanceConfig = instanceService.findInstanceConfig(someInstanceId,
someConfigAppId, someConfigNamespaceName); someConfigAppId, someConfigClusterName, someConfigNamespaceName);
assertNull(instanceConfig); assertNull(instanceConfig);
instanceService.createInstanceConfig(assembleInstanceConfig(someInstanceId, someConfigAppId, instanceService.createInstanceConfig(assembleInstanceConfig(someInstanceId, someConfigAppId,
someConfigNamespaceName, someReleaseKey)); someConfigClusterName, someConfigNamespaceName, someReleaseKey));
instanceConfig = instanceService.findInstanceConfig(someInstanceId, someConfigAppId, instanceConfig = instanceService.findInstanceConfig(someInstanceId, someConfigAppId,
someConfigNamespaceName); someConfigClusterName, someConfigNamespaceName);
assertNotEquals(0, instanceConfig.getId()); assertNotEquals(0, instanceConfig.getId());
assertEquals(someReleaseKey, instanceConfig.getReleaseKey()); assertEquals(someReleaseKey, instanceConfig.getReleaseKey());
...@@ -102,7 +104,7 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -102,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,
someConfigNamespaceName); someConfigClusterName, someConfigNamespaceName);
assertEquals(instanceConfig.getId(), updated.getId()); assertEquals(instanceConfig.getId(), updated.getId());
assertEquals(anotherReleaseKey, updated.getReleaseKey()); assertEquals(anotherReleaseKey, updated.getReleaseKey());
...@@ -114,30 +116,102 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -114,30 +116,102 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
long someInstanceId = 1; long someInstanceId = 1;
long anotherInstanceId = 2; long anotherInstanceId = 2;
String someConfigAppId = "someConfigAppId"; String someConfigAppId = "someConfigAppId";
String someConfigClusterName = "someConfigClusterName";
String someConfigNamespaceName = "someConfigNamespaceName"; String someConfigNamespaceName = "someConfigNamespaceName";
String someReleaseKey = "someReleaseKey";
Date someValidDate = new Date(); Date someValidDate = new Date();
Pageable pageable = new PageRequest(0, 10); Pageable pageable = new PageRequest(0, 10);
String someReleaseKey = "someReleaseKey";
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -2); calendar.add(Calendar.DATE, -2);
Date someInvalidDate = calendar.getTime(); Date someInvalidDate = calendar.getTime();
InstanceConfig someValidConfig = assembleInstanceConfig(someInstanceId, someConfigAppId, prepareInstanceConfigForInstance(someInstanceId, someConfigAppId, someConfigClusterName,
someConfigNamespaceName, someReleaseKey); someConfigNamespaceName, someReleaseKey, someValidDate);
someValidConfig.setDataChangeCreatedTime(someValidDate); prepareInstanceConfigForInstance(anotherInstanceId, someConfigAppId, someConfigClusterName,
InstanceConfig someInvalidConfig = assembleInstanceConfig(anotherInstanceId, someConfigAppId, someConfigNamespaceName, someReleaseKey, someInvalidDate);
someConfigNamespaceName, someReleaseKey);
someInvalidConfig.setDataChangeCreatedTime(someInvalidDate);
instanceService.createInstanceConfig(someValidConfig);
instanceService.createInstanceConfig(someInvalidConfig);
List<InstanceConfig> validInstanceConfigs = instanceService Page<InstanceConfig> validInstanceConfigs = instanceService
.findActiveInstanceConfigsByReleaseKey(someReleaseKey, pageable); .findActiveInstanceConfigsByReleaseKey(someReleaseKey, pageable);
assertEquals(1, validInstanceConfigs.size()); assertEquals(1, validInstanceConfigs.getContent().size());
assertEquals(someInstanceId, validInstanceConfigs.get(0).getInstanceId()); assertEquals(someInstanceId, validInstanceConfigs.getContent().get(0).getInstanceId());
}
@Test
@Rollback
public void testFindInstancesByNamespace() throws Exception {
String someConfigAppId = "someConfigAppId";
String someConfigClusterName = "someConfigClusterName";
String someConfigNamespaceName = "someConfigNamespaceName";
String someReleaseKey = "someReleaseKey";
Date someValidDate = new Date();
String someAppId = "someAppId";
String someClusterName = "someClusterName";
String someDataCenter = "someDataCenter";
String someIp = "someIp";
String anotherIp = "anotherIp";
Instance someInstance = instanceService.createInstance(assembleInstance(someAppId,
someClusterName, someDataCenter, someIp));
Instance anotherInstance = instanceService.createInstance(assembleInstance(someAppId,
someClusterName, someDataCenter, anotherIp));
prepareInstanceConfigForInstance(someInstance.getId(), someConfigAppId, someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
prepareInstanceConfigForInstance(anotherInstance.getId(), someConfigAppId,
someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
Page<Instance> result = instanceService.findInstancesByNamespace(someConfigAppId,
someConfigClusterName, someConfigNamespaceName, new PageRequest(0, 10));
assertEquals(Lists.newArrayList(someInstance, anotherInstance), result.getContent());
}
@Test
@Rollback
public void testFindInstanceConfigsByNamespaceWithReleaseKeysNotIn() throws Exception {
long someInstanceId = 1;
long anotherInstanceId = 2;
long yetAnotherInstanceId = 3;
String someConfigAppId = "someConfigAppId";
String someConfigClusterName = "someConfigClusterName";
String someConfigNamespaceName = "someConfigNamespaceName";
Date someValidDate = new Date();
String someReleaseKey = "someReleaseKey";
String anotherReleaseKey = "anotherReleaseKey";
String yetAnotherReleaseKey = "yetAnotherReleaseKey";
InstanceConfig someInstanceConfig = prepareInstanceConfigForInstance(someInstanceId,
someConfigAppId, someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
InstanceConfig anotherInstanceConfig = prepareInstanceConfigForInstance(anotherInstanceId,
someConfigAppId, someConfigClusterName,
someConfigNamespaceName, someReleaseKey, someValidDate);
prepareInstanceConfigForInstance(yetAnotherInstanceId, someConfigAppId, someConfigClusterName,
someConfigNamespaceName, anotherReleaseKey, someValidDate);
List<InstanceConfig> instanceConfigs = instanceService
.findInstanceConfigsByNamespaceWithReleaseKeysNotIn(someConfigAppId,
someConfigClusterName, someConfigNamespaceName, Sets.newHashSet(anotherReleaseKey,
yetAnotherReleaseKey));
assertEquals(Lists.newArrayList(someInstanceConfig, anotherInstanceConfig), instanceConfigs);
}
private InstanceConfig prepareInstanceConfigForInstance(long instanceId, String configAppId,
String configClusterName, String
configNamespace, String releaseKey,
Date lastModifiedTime) {
InstanceConfig someConfig = assembleInstanceConfig(instanceId, configAppId, configClusterName,
configNamespace, releaseKey);
someConfig.setDataChangeCreatedTime(lastModifiedTime);
someConfig.setDataChangeLastModifiedTime(lastModifiedTime);
return instanceService.createInstanceConfig(someConfig);
} }
private Instance assembleInstance(String appId, String clusterName, String dataCenter, String private Instance assembleInstance(String appId, String clusterName, String dataCenter, String
...@@ -152,12 +226,13 @@ public class InstanceServiceTest extends AbstractIntegrationTest { ...@@ -152,12 +226,13 @@ public class InstanceServiceTest extends AbstractIntegrationTest {
} }
private InstanceConfig assembleInstanceConfig(long instanceId, String configAppId, String private InstanceConfig assembleInstanceConfig(long instanceId, String configAppId, String
configNamespaceName, String releaseKey) { configClusterName, String configNamespaceName, String releaseKey) {
InstanceConfig instanceConfig = new InstanceConfig(); InstanceConfig instanceConfig = new InstanceConfig();
instanceConfig.setInstanceId(instanceId); instanceConfig.setInstanceId(instanceId);
instanceConfig.setConfigAppId(configAppId); instanceConfig.setConfigAppId(configAppId);
instanceConfig.setConfigClusterName(configClusterName);
instanceConfig.setConfigNamespaceName(configNamespaceName); instanceConfig.setConfigNamespaceName(configNamespaceName);
instanceConfig.setReleaseKey(releaseKey); instanceConfig.setReleaseKey(releaseKey);
return instanceConfig; return instanceConfig;
} }
} }
\ No newline at end of file
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.biz.AbstractUnitTest; import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.Release; import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
...@@ -13,9 +16,12 @@ import org.mockito.Mock; ...@@ -13,9 +16,12 @@ import org.mockito.Mock;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
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;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
...@@ -147,6 +153,38 @@ public class ReleaseServiceTest extends AbstractUnitTest { ...@@ -147,6 +153,38 @@ public class ReleaseServiceTest extends AbstractUnitTest {
someAppId, someClusterName, someNamespaceName); someAppId, someClusterName, someNamespaceName);
} }
@Test
public void testFindByReleaseIds() throws Exception {
Release someRelease = mock(Release.class);
Release anotherRelease = mock(Release.class);
long someReleaseId = 1;
long anotherReleaseId = 2;
List<Release> someReleases = Lists.newArrayList(someRelease, anotherRelease);
Set<Long> someReleaseIds = Sets.newHashSet(someReleaseId, anotherReleaseId);
when(releaseRepository.findAll(someReleaseIds)).thenReturn(someReleases);
List<Release> result = releaseService.findByReleaseIds(someReleaseIds);
assertEquals(someReleases, result);
}
@Test
public void testFindByReleaseKeys() throws Exception {
Release someRelease = mock(Release.class);
Release anotherRelease = mock(Release.class);
String someReleaseKey = "key1";
String anotherReleaseKey = "key2";
List<Release> someReleases = Lists.newArrayList(someRelease, anotherRelease);
Set<String> someReleaseKeys = Sets.newHashSet(someReleaseKey, anotherReleaseKey);
when(releaseRepository.findByReleaseKeyIn(someReleaseKeys)).thenReturn(someReleases);
List<Release> result = releaseService.findByReleaseKeys(someReleaseKeys);
assertEquals(someReleases, result);
}
private Release assembleRelease(long releaseId, String releaseKey, String appId, private Release assembleRelease(long releaseId, String releaseKey, String appId,
String clusterName, String clusterName,
String groupName, String configurations) { String groupName, String configurations) {
......
package com.ctrip.framework.apollo.common.dto;
import org.springframework.data.domain.Pageable;
import java.util.Collections;
import java.util.List;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class PageDTO<T> {
private final long total;
private final List<T> content;
private final int page;
private final int size;
public PageDTO(List<T> content, Pageable pageable, long total) {
this.total = total;
this.content = content;
this.page = pageable.getPageNumber();
this.size = pageable.getPageSize();
}
public long getTotal() {
return total;
}
public List<T> getContent() {
return Collections.unmodifiableList(content);
}
public int getPage() {
return page;
}
public int getSize() {
return size;
}
}
...@@ -217,7 +217,7 @@ public class ConfigController { ...@@ -217,7 +217,7 @@ public class ConfigController {
return; return;
} }
for (Release release : releases) { for (Release release : releases) {
instanceConfigAuditUtil.audit(appId, cluster, datacenter, clientIp, release.getAppId(), instanceConfigAuditUtil.audit(appId, cluster, datacenter, clientIp, release.getAppId(), release.getClusterName(),
release.getNamespaceName(), release.getReleaseKey()); release.getNamespaceName(), release.getReleaseKey());
} }
} }
......
...@@ -58,15 +58,14 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -58,15 +58,14 @@ public class InstanceConfigAuditUtil implements InitializingBean {
} }
public boolean audit(String appId, String clusterName, String dataCenter, String public boolean audit(String appId, String clusterName, String dataCenter, String
ip, String configAppId, String configNamespace, String releaseKey) { ip, String configAppId, String configClusterName, String configNamespace, String releaseKey) {
return this.audits.offer(new InstanceConfigAuditModel(appId, clusterName, dataCenter, ip, return this.audits.offer(new InstanceConfigAuditModel(appId, clusterName, dataCenter, ip,
configAppId, configNamespace, releaseKey)); configAppId, configClusterName, configNamespace, releaseKey));
} }
void doAudit(InstanceConfigAuditModel auditModel) { void doAudit(InstanceConfigAuditModel auditModel) {
String instanceCacheKey = assembleInstanceKey(auditModel.getAppId(), auditModel String instanceCacheKey = assembleInstanceKey(auditModel.getAppId(), auditModel
.getClusterName(), .getClusterName(), auditModel.getIp(), auditModel.getDataCenter());
auditModel.getIp(), auditModel.getDataCenter());
Long instanceId = instanceCache.getIfPresent(instanceCacheKey); Long instanceId = instanceCache.getIfPresent(instanceCacheKey);
if (instanceId == null) { if (instanceId == null) {
instanceId = prepareInstanceId(auditModel); instanceId = prepareInstanceId(auditModel);
...@@ -75,7 +74,7 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -75,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.getConfigNamespace()); .getConfigAppId(), auditModel.getConfigClusterName(), 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
...@@ -87,7 +86,7 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -87,7 +86,7 @@ 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.getConfigNamespace()); .getConfigAppId(), auditModel.getConfigClusterName(), auditModel.getConfigNamespace());
//we need to update no matter the release key is the same or not, to ensure the //we need to update no matter the release key is the same or not, to ensure the
//last modified time is updated each day //last modified time is updated each day
...@@ -101,6 +100,7 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -101,6 +100,7 @@ public class InstanceConfigAuditUtil implements InitializingBean {
instanceConfig = new InstanceConfig(); instanceConfig = new InstanceConfig();
instanceConfig.setInstanceId(instanceId); instanceConfig.setInstanceId(instanceId);
instanceConfig.setConfigAppId(auditModel.getConfigAppId()); instanceConfig.setConfigAppId(auditModel.getConfigAppId());
instanceConfig.setConfigClusterName(auditModel.getConfigClusterName());
instanceConfig.setConfigNamespaceName(auditModel.getConfigNamespace()); instanceConfig.setConfigNamespaceName(auditModel.getConfigNamespace());
instanceConfig.setReleaseKey(auditModel.getReleaseKey()); instanceConfig.setReleaseKey(auditModel.getReleaseKey());
...@@ -160,8 +160,9 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -160,8 +160,9 @@ public class InstanceConfigAuditUtil implements InitializingBean {
} }
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 {
...@@ -170,16 +171,19 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -170,16 +171,19 @@ public class InstanceConfigAuditUtil implements InitializingBean {
private String dataCenter; private String dataCenter;
private String ip; private String ip;
private String configAppId; private String configAppId;
private String configClusterName;
private String configNamespace; private String configNamespace;
private String releaseKey; private String releaseKey;
public InstanceConfigAuditModel(String appId, String clusterName, String dataCenter, String public InstanceConfigAuditModel(String appId, String clusterName, String dataCenter, String
clientIp, String configAppId, String configNamespace, String releaseKey) { clientIp, String configAppId, String configClusterName, String configNamespace, String
releaseKey) {
this.appId = appId; this.appId = appId;
this.clusterName = clusterName; this.clusterName = clusterName;
this.dataCenter = Strings.isNullOrEmpty(dataCenter) ? "" : dataCenter; this.dataCenter = Strings.isNullOrEmpty(dataCenter) ? "" : dataCenter;
this.ip = clientIp; this.ip = clientIp;
this.configAppId = configAppId; this.configAppId = configAppId;
this.configClusterName = configClusterName;
this.configNamespace = configNamespace; this.configNamespace = configNamespace;
this.releaseKey = releaseKey; this.releaseKey = releaseKey;
} }
...@@ -212,6 +216,10 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -212,6 +216,10 @@ public class InstanceConfigAuditUtil implements InitializingBean {
return releaseKey; return releaseKey;
} }
public String getConfigClusterName() {
return configClusterName;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
...@@ -222,13 +230,15 @@ public class InstanceConfigAuditUtil implements InitializingBean { ...@@ -222,13 +230,15 @@ public class InstanceConfigAuditUtil implements InitializingBean {
Objects.equals(dataCenter, model.dataCenter) && Objects.equals(dataCenter, model.dataCenter) &&
Objects.equals(ip, model.ip) && Objects.equals(ip, model.ip) &&
Objects.equals(configAppId, model.configAppId) && Objects.equals(configAppId, model.configAppId) &&
Objects.equals(configClusterName, model.configClusterName) &&
Objects.equals(configNamespace, model.configNamespace) && Objects.equals(configNamespace, model.configNamespace) &&
Objects.equals(releaseKey, model.releaseKey); Objects.equals(releaseKey, model.releaseKey);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(appId, clusterName, dataCenter, ip, configAppId, configNamespace, return Objects.hash(appId, clusterName, dataCenter, ip, configAppId, configClusterName,
configNamespace,
releaseKey); releaseKey);
} }
} }
......
...@@ -113,7 +113,7 @@ public class ConfigControllerTest { ...@@ -113,7 +113,7 @@ public class ConfigControllerTest {
assertEquals(defaultNamespaceName, result.getNamespaceName()); assertEquals(defaultNamespaceName, result.getNamespaceName());
assertEquals(someServerSideNewReleaseKey, result.getReleaseKey()); assertEquals(someServerSideNewReleaseKey, result.getReleaseKey());
verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter, verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter,
someClientIp, someAppId, defaultNamespaceName, someServerSideNewReleaseKey); someClientIp, someAppId, someClusterName, defaultNamespaceName, someServerSideNewReleaseKey);
} }
@Test @Test
...@@ -283,6 +283,7 @@ public class ConfigControllerTest { ...@@ -283,6 +283,7 @@ public class ConfigControllerTest {
String someServerSideReleaseKey = "2"; String someServerSideReleaseKey = "2";
HttpServletResponse someResponse = mock(HttpServletResponse.class); HttpServletResponse someResponse = mock(HttpServletResponse.class);
String somePublicAppId = "somePublicAppId"; String somePublicAppId = "somePublicAppId";
String somePublicClusterName = "somePublicClusterName";
AppNamespace somePublicAppNamespace = AppNamespace somePublicAppNamespace =
assemblePublicAppNamespace(somePublicAppId, somePublicNamespaceName); assemblePublicAppNamespace(somePublicAppId, somePublicNamespaceName);
...@@ -294,6 +295,7 @@ public class ConfigControllerTest { ...@@ -294,6 +295,7 @@ public class ConfigControllerTest {
.thenReturn(somePublicRelease); .thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey); when(somePublicRelease.getReleaseKey()).thenReturn(someServerSideReleaseKey);
when(somePublicRelease.getAppId()).thenReturn(somePublicAppId); when(somePublicRelease.getAppId()).thenReturn(somePublicAppId);
when(somePublicRelease.getClusterName()).thenReturn(somePublicClusterName);
when(somePublicRelease.getNamespaceName()).thenReturn(somePublicNamespaceName); when(somePublicRelease.getNamespaceName()).thenReturn(somePublicNamespaceName);
ApolloConfig result = configController ApolloConfig result = configController
...@@ -306,7 +308,7 @@ public class ConfigControllerTest { ...@@ -306,7 +308,7 @@ public class ConfigControllerTest {
assertEquals(somePublicNamespaceName, result.getNamespaceName()); assertEquals(somePublicNamespaceName, result.getNamespaceName());
assertEquals("foo", result.getConfigurations().get("apollo.public.bar")); assertEquals("foo", result.getConfigurations().get("apollo.public.bar"));
verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter, verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter,
someClientIp, somePublicAppId, somePublicNamespaceName, someServerSideReleaseKey); someClientIp, somePublicAppId, somePublicClusterName, somePublicNamespaceName, someServerSideReleaseKey);
} }
@Test @Test
...@@ -396,6 +398,7 @@ public class ConfigControllerTest { ...@@ -396,6 +398,7 @@ public class ConfigControllerTest {
.thenReturn(somePublicRelease); .thenReturn(somePublicRelease);
when(somePublicRelease.getReleaseKey()).thenReturn(somePublicAppSideReleaseKey); when(somePublicRelease.getReleaseKey()).thenReturn(somePublicAppSideReleaseKey);
when(somePublicRelease.getAppId()).thenReturn(somePublicAppId); when(somePublicRelease.getAppId()).thenReturn(somePublicAppId);
when(somePublicRelease.getClusterName()).thenReturn(someDataCenter);
when(somePublicRelease.getNamespaceName()).thenReturn(somePublicNamespaceName); when(somePublicRelease.getNamespaceName()).thenReturn(somePublicNamespaceName);
ApolloConfig result = ApolloConfig result =
...@@ -412,9 +415,9 @@ public class ConfigControllerTest { ...@@ -412,9 +415,9 @@ public class ConfigControllerTest {
assertEquals("foo-override", result.getConfigurations().get("apollo.public.foo")); assertEquals("foo-override", result.getConfigurations().get("apollo.public.foo"));
assertEquals("bar", result.getConfigurations().get("apollo.public.bar")); assertEquals("bar", result.getConfigurations().get("apollo.public.bar"));
verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter, verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter,
someClientIp, someAppId, somePublicNamespaceName, someAppSideReleaseKey); someClientIp, someAppId, someClusterName, somePublicNamespaceName, someAppSideReleaseKey);
verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter, verify(instanceConfigAuditUtil, times(1)).audit(someAppId, someClusterName, someDataCenter,
someClientIp, somePublicAppId, somePublicNamespaceName, somePublicAppSideReleaseKey); someClientIp, somePublicAppId, someDataCenter, somePublicNamespaceName, somePublicAppSideReleaseKey);
} }
@Test @Test
......
...@@ -36,6 +36,7 @@ public class InstanceConfigAuditUtilTest { ...@@ -36,6 +36,7 @@ public class InstanceConfigAuditUtilTest {
private BlockingQueue<InstanceConfigAuditUtil.InstanceConfigAuditModel> audits; private BlockingQueue<InstanceConfigAuditUtil.InstanceConfigAuditModel> audits;
private String someAppId; private String someAppId;
private String someConfigClusterName;
private String someClusterName; private String someClusterName;
private String someDataCenter; private String someDataCenter;
private String someIp; private String someIp;
...@@ -59,18 +60,19 @@ public class InstanceConfigAuditUtilTest { ...@@ -59,18 +60,19 @@ public class InstanceConfigAuditUtilTest {
someDataCenter = "someDataCenter"; someDataCenter = "someDataCenter";
someIp = "someIp"; someIp = "someIp";
someConfigAppId = "someConfigAppId"; someConfigAppId = "someConfigAppId";
someConfigClusterName= "someConfigClusterName";
someConfigNamespace = "someConfigNamespace"; someConfigNamespace = "someConfigNamespace";
someReleaseKey = "someReleaseKey"; someReleaseKey = "someReleaseKey";
someAuditModel = new InstanceConfigAuditUtil.InstanceConfigAuditModel(someAppId, someAuditModel = new InstanceConfigAuditUtil.InstanceConfigAuditModel(someAppId,
someClusterName, someDataCenter, someIp, someConfigAppId, someConfigNamespace, someClusterName, someDataCenter, someIp, someConfigAppId, someConfigClusterName, someConfigNamespace,
someReleaseKey); someReleaseKey);
} }
@Test @Test
public void testAudit() throws Exception { public void testAudit() throws Exception {
boolean result = instanceConfigAuditUtil.audit(someAppId, someClusterName, someDataCenter, boolean result = instanceConfigAuditUtil.audit(someAppId, someClusterName, someDataCenter,
someIp, someConfigAppId, someConfigNamespace, someReleaseKey); someIp, someConfigAppId, someConfigClusterName, someConfigNamespace, someReleaseKey);
InstanceConfigAuditUtil.InstanceConfigAuditModel audit = audits.poll(); InstanceConfigAuditUtil.InstanceConfigAuditModel audit = audits.poll();
...@@ -91,10 +93,10 @@ public class InstanceConfigAuditUtilTest { ...@@ -91,10 +93,10 @@ 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, verify(instanceService, times(1)).findInstanceConfig(someInstanceId, someConfigAppId, someConfigClusterName,
someConfigNamespace); someConfigNamespace);
verify(instanceService, times(1)).createInstanceConfig(any(InstanceConfig.class)); verify(instanceService, times(1)).createInstanceConfig(any(InstanceConfig.class));
} }
} }
\ No newline at end of file
package com.ctrip.framework.apollo.portal.api; package com.ctrip.framework.apollo.portal.api;
import com.google.common.base.Joiner;
import com.ctrip.framework.apollo.common.dto.AppDTO; import com.ctrip.framework.apollo.common.dto.AppDTO;
import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO; import com.ctrip.framework.apollo.common.dto.AppNamespaceDTO;
import com.ctrip.framework.apollo.common.dto.ClusterDTO; import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.common.dto.CommitDTO; import com.ctrip.framework.apollo.common.dto.CommitDTO;
import com.ctrip.framework.apollo.common.dto.InstanceDTO;
import com.ctrip.framework.apollo.common.dto.ItemChangeSets; import com.ctrip.framework.apollo.common.dto.ItemChangeSets;
import com.ctrip.framework.apollo.common.dto.ItemDTO; import com.ctrip.framework.apollo.common.dto.ItemDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO; import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO; import com.ctrip.framework.apollo.common.dto.NamespaceLockDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.dto.ReleaseDTO; import com.ctrip.framework.apollo.common.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import java.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
@Service @Service
...@@ -237,4 +245,39 @@ public class AdminServiceAPI { ...@@ -237,4 +245,39 @@ public class AdminServiceAPI {
} }
} }
@Service
public static class InstanceAPI extends API {
private Joiner joiner = Joiner.on(",");
private ParameterizedTypeReference<PageDTO<InstanceDTO>> pageInstanceDtoType = new ParameterizedTypeReference<PageDTO<InstanceDTO>>() {};
public PageDTO<InstanceDTO> getByRelease(Env env, long releaseId, int page, int size){
ResponseEntity<PageDTO<InstanceDTO>> entity = restTemplate.get(env, "/instances/by-release?releaseId={releaseId}&page={page}&size={size}", pageInstanceDtoType, releaseId, page, size);
return entity.getBody();
}
public List<InstanceDTO> getByReleasesNotIn(String appId, Env env, String clusterName, String namespaceName, Set<Long> releaseIds){
InstanceDTO[] instanceDTOs = restTemplate.get(env, "/instances/by-namespace-and-releases-not-in?appId={appId}&clusterName={clusterName}&namespaceName={namespaceName}&releaseIds={releaseIds}",
InstanceDTO[].class, appId, clusterName, namespaceName, joiner.join(releaseIds));
return Arrays.asList(instanceDTOs);
}
public PageDTO<InstanceDTO> getByNamespace(String appId, Env env, String clusterName, String namespaceName, int page, int size){
ResponseEntity<PageDTO<InstanceDTO>> entity = restTemplate.get(env, "/instances/by-namespace?appId={appId}&clusterName={clusterName}&namespaceName={namespaceName}&page={page}&size={size}",
pageInstanceDtoType, appId, clusterName, namespaceName, page, size);
return entity.getBody();
}
public int getInstanceCountByNamespace(String appId, Env env, String clusterName, String namespaceName){
Integer count = restTemplate.get(env, "/instances/by-namespace/count?appId={appId}&clusterName={clusterName}&namespaceName={namespaceName}",
Integer.class, appId, clusterName, namespaceName);
if (count == null){
return 0;
}
return count;
}
}
} }
...@@ -31,7 +31,8 @@ import javax.annotation.PostConstruct; ...@@ -31,7 +31,8 @@ import javax.annotation.PostConstruct;
public class AdminServiceAddressLocator { public class AdminServiceAddressLocator {
private static final int DEFAULT_TIMEOUT_MS = 1000; private static final int DEFAULT_TIMEOUT_MS = 1000;
private static final long REFRESH_INTERVAL = 5 * 60 * 1000; private static final long NORMAL_REFRESH_INTERVAL = 5 * 60 * 1000;
private static final long OFFLINE_REFRESH_INTERVAL = 10 * 1000;
private static final int RETRY_TIMES = 3; private static final int RETRY_TIMES = 3;
private static final String ADMIN_SERVICE_URL_PATH = "/services/admin"; private static final String ADMIN_SERVICE_URL_PATH = "/services/admin";
...@@ -65,9 +66,7 @@ public class AdminServiceAddressLocator { ...@@ -65,9 +66,7 @@ public class AdminServiceAddressLocator {
refreshServiceAddressService = Executors.newScheduledThreadPool(1); refreshServiceAddressService = Executors.newScheduledThreadPool(1);
refreshServiceAddressService.scheduleWithFixedDelay( refreshServiceAddressService.schedule(new RefreshAdminServerAddressTask(), 1, TimeUnit.MILLISECONDS);
new RefreshAdminServerAddressTask(), 0, REFRESH_INTERVAL,
TimeUnit.MILLISECONDS);
} }
public List<ServiceDTO> getServiceList(Env env) { public List<ServiceDTO> getServiceList(Env env) {
...@@ -85,13 +84,22 @@ public class AdminServiceAddressLocator { ...@@ -85,13 +84,22 @@ public class AdminServiceAddressLocator {
@Override @Override
public void run() { public void run() {
boolean refreshSuccess = true;
//refresh fail if get any env address fail
for (Env env : allEnvs) { for (Env env : allEnvs) {
refreshServerAddressCache(env); boolean currentEnvRefreshResult = refreshServerAddressCache(env);
refreshSuccess = refreshSuccess && currentEnvRefreshResult;
}
if (refreshSuccess){
refreshServiceAddressService.schedule(new RefreshAdminServerAddressTask(), NORMAL_REFRESH_INTERVAL, TimeUnit.MILLISECONDS);
} else {
refreshServiceAddressService.schedule(new RefreshAdminServerAddressTask(), OFFLINE_REFRESH_INTERVAL, TimeUnit.MILLISECONDS);
} }
} }
} }
private void refreshServerAddressCache(Env env) { private boolean refreshServerAddressCache(Env env) {
for (int i = 0; i < RETRY_TIMES; i++) { for (int i = 0; i < RETRY_TIMES; i++) {
...@@ -101,12 +109,13 @@ public class AdminServiceAddressLocator { ...@@ -101,12 +109,13 @@ public class AdminServiceAddressLocator {
continue; continue;
} }
cache.put(env, Arrays.asList(services)); cache.put(env, Arrays.asList(services));
break; return true;
} catch (Throwable e) {//meta server error } catch (Throwable e) {//meta server error
Cat.logError("get admin server address fail", e); Cat.logError("get admin server address fail", e);
continue; continue;
} }
} }
return false;
} }
private ServiceDTO[] getAdminServerAddress(Env env) { private ServiceDTO[] getAdminServerAddress(Env env) {
......
...@@ -11,7 +11,9 @@ import com.dianping.cat.message.Transaction; ...@@ -11,7 +11,9 @@ import com.dianping.cat.message.Transaction;
import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException; import org.apache.http.conn.HttpHostConnectException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientException;
...@@ -50,6 +52,13 @@ public class RetryableRestTemplate { ...@@ -50,6 +52,13 @@ public class RetryableRestTemplate {
return execute(HttpMethod.GET, env, path, null, responseType, urlVariables); return execute(HttpMethod.GET, env, path, null, responseType, urlVariables);
} }
public <T> ResponseEntity<T> get(Env env, String path, ParameterizedTypeReference<T> reference,
Object... uriVariables)
throws RestClientException {
return execute(env, path, reference, uriVariables);
}
public <T> T post(Env env, String path, Object request, Class<T> responseType, Object... uriVariables) public <T> T post(Env env, String path, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException { throws RestClientException {
return execute(HttpMethod.POST, env, path, request, responseType, uriVariables); return execute(HttpMethod.POST, env, path, request, responseType, uriVariables);
...@@ -73,14 +82,7 @@ public class RetryableRestTemplate { ...@@ -73,14 +82,7 @@ public class RetryableRestTemplate {
String uri = uriTemplateHandler.expand(path, uriVariables).getPath(); String uri = uriTemplateHandler.expand(path, uriVariables).getPath();
Transaction ct = Cat.newTransaction("AdminAPI", uri); Transaction ct = Cat.newTransaction("AdminAPI", uri);
List<ServiceDTO> services = adminServiceAddressLocator.getServiceList(env); List<ServiceDTO> services = getAdminServices(env, ct);
if (CollectionUtils.isEmpty(services)) {
ServiceException e = new ServiceException("No available admin service");
ct.setStatus(e);
ct.complete();
throw e;
}
for (ServiceDTO serviceDTO : services) { for (ServiceDTO serviceDTO : services) {
try { try {
...@@ -110,6 +112,55 @@ public class RetryableRestTemplate { ...@@ -110,6 +112,55 @@ public class RetryableRestTemplate {
throw e; throw e;
} }
private <T> ResponseEntity<T> execute(Env env, String path, ParameterizedTypeReference<T> reference,
Object... uriVariables){
if (path.startsWith("/")) {
path = path.substring(1, path.length());
}
String uri = uriTemplateHandler.expand(path, uriVariables).getPath();
Transaction ct = Cat.newTransaction("AdminAPI", uri);
List<ServiceDTO> services = getAdminServices(env, ct);
for (ServiceDTO serviceDTO : services) {
try {
ResponseEntity<T> result =
restTemplate.exchange(parseHost(serviceDTO) + path, HttpMethod.GET, null, reference, uriVariables);
ct.setStatus(Message.SUCCESS);
ct.complete();
return result;
} catch (Throwable t) {
Cat.logError(t);
Cat.logEvent(CatEventType.API_RETRY, uri);
continue;
}
}
//all admin server down
ServiceException e = new ServiceException("No available admin service");
ct.setStatus(e);
ct.complete();
throw e;
}
private List<ServiceDTO> getAdminServices( Env env, Transaction ct){
List<ServiceDTO> services = adminServiceAddressLocator.getServiceList(env);
if (CollectionUtils.isEmpty(services)) {
ServiceException e = new ServiceException("No available admin service");
ct.setStatus(e);
ct.complete();
throw e;
}
return services;
}
private <T> T doExecute(HttpMethod method, ServiceDTO service, String path, Object request, private <T> T doExecute(HttpMethod method, ServiceDTO service, String path, Object request,
Class<T> responseType, Class<T> responseType,
Object... uriVariables) { Object... uriVariables) {
...@@ -143,11 +194,11 @@ public class RetryableRestTemplate { ...@@ -143,11 +194,11 @@ public class RetryableRestTemplate {
Throwable nestedException = e.getCause(); Throwable nestedException = e.getCause();
if (method == HttpMethod.GET) { if (method == HttpMethod.GET) {
return nestedException instanceof SocketTimeoutException return nestedException instanceof SocketTimeoutException
|| nestedException instanceof HttpHostConnectException || nestedException instanceof HttpHostConnectException
|| nestedException instanceof ConnectTimeoutException; || nestedException instanceof ConnectTimeoutException;
} else { } else {
return nestedException instanceof HttpHostConnectException return nestedException instanceof HttpHostConnectException
|| nestedException instanceof ConnectTimeoutException; || nestedException instanceof ConnectTimeoutException;
} }
} }
......
package com.ctrip.framework.apollo.portal.controller;
import com.google.common.base.Splitter;
import com.ctrip.framework.apollo.common.dto.InstanceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.entity.vo.Number;
import com.ctrip.framework.apollo.portal.service.InstanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RestController
public class InstanceController {
private static final Splitter RELEASES_SPLITTER = Splitter.on(",").omitEmptyStrings()
.trimResults();
@Autowired
private InstanceService instanceService;
@RequestMapping("/envs/{env}/instances/by-release")
public PageDTO<InstanceDTO> getByRelease(@PathVariable String env, @RequestParam long releaseId,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) {
return instanceService.getByRelease(Env.valueOf(env), releaseId, page, size);
}
@RequestMapping("/envs/{env}/instances/by-namespace")
public PageDTO<InstanceDTO> getByNamespace(@PathVariable String env, @RequestParam String appId,
@RequestParam String clusterName, @RequestParam String namespaceName,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) {
return instanceService.getByNamespace(Env.valueOf(env), appId, clusterName, namespaceName, page, size);
}
@RequestMapping("/envs/{env}/instances/by-namespace/count")
public ResponseEntity<Number> getInstanceCountByNamespace(@PathVariable String env, @RequestParam String appId,
@RequestParam String clusterName, @RequestParam String namespaceName) {
int count = instanceService.getInstanceCountByNamepsace(appId, Env.valueOf(env), clusterName, namespaceName);
return ResponseEntity.ok(new Number(count));
}
@RequestMapping("/envs/{env}/instances/by-namespace-and-releases-not-in")
public List<InstanceDTO> getByReleasesNotIn(@PathVariable String env, @RequestParam String appId,
@RequestParam String clusterName, @RequestParam String namespaceName,
@RequestParam String releaseIds) {
Set<Long> releaseIdSet = RELEASES_SPLITTER.splitToList(releaseIds).stream().map(Long::parseLong)
.collect(Collectors.toSet());
if (CollectionUtils.isEmpty(releaseIdSet)){
throw new BadRequestException("release ids can not be empty");
}
return instanceService.getByReleasesNotIn(Env.valueOf(env), appId, clusterName, namespaceName, releaseIdSet);
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
public class Number {
private int num;
public Number(int num){
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
package com.ctrip.framework.apollo.portal.service;
import com.ctrip.framework.apollo.common.dto.InstanceDTO;
import com.ctrip.framework.apollo.common.dto.PageDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
@Service
public class InstanceService {
@Autowired
private AdminServiceAPI.InstanceAPI instanceAPI;
public PageDTO<InstanceDTO> getByRelease(Env env, long releaseId, int page, int size){
return instanceAPI.getByRelease(env, releaseId, page, size);
}
public PageDTO<InstanceDTO> getByNamespace(Env env, String appId, String clusterName, String namespaceName, int page, int size){
return instanceAPI.getByNamespace(appId, env, clusterName, namespaceName, page, size);
}
public int getInstanceCountByNamepsace(String appId, Env env, String clusterName, String namespaceName){
return instanceAPI.getInstanceCountByNamespace(appId, env, clusterName, namespaceName);
}
public List<InstanceDTO> getByReleasesNotIn(Env env, String appId, String clusterName, String namespaceName, Set<Long> releaseIds){
return instanceAPI.getByReleasesNotIn(appId, env, clusterName, namespaceName, releaseIds);
}
}
...@@ -254,8 +254,7 @@ ...@@ -254,8 +254,7 @@
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header panel-primary"> <div class="modal-header panel-primary">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
ng-click="cancelEdit()"><span
aria-hidden="true">&times;</span></button> aria-hidden="true">&times;</span></button>
<h4 class="modal-title"> <h4 class="modal-title">
<span ng-show="tableViewOperType == 'create'"> 添加配置项</span> <span ng-show="tableViewOperType == 'create'"> 添加配置项</span>
...@@ -310,7 +309,7 @@ ...@@ -310,7 +309,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
</button> </button>
<button type="button" class="btn btn-default" data-dismiss="modal" ng-click="cancelEdit()"> <button type="button" class="btn btn-default" data-dismiss="modal" >
关闭 关闭
</button> </button>
<button type="submit" class="btn btn-primary" <button type="submit" class="btn btn-primary"
...@@ -470,6 +469,7 @@ ...@@ -470,6 +469,7 @@
<script type="application/javascript" src="scripts/services/PermissionService.js"></script> <script type="application/javascript" src="scripts/services/PermissionService.js"></script>
<script type="application/javascript" src="scripts/services/CommitService.js"></script> <script type="application/javascript" src="scripts/services/CommitService.js"></script>
<script type="application/javascript" src="scripts/services/NamespaceLockService.js"></script> <script type="application/javascript" src="scripts/services/NamespaceLockService.js"></script>
<script type="application/javascript" src="scripts/services/InstanceService.js"></script>
<script type="application/javascript" src="scripts/AppUtils.js"></script> <script type="application/javascript" src="scripts/AppUtils.js"></script>
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<span class="label label-primary no-radius" <span class="label label-primary no-radius"
ng-if="release.active">当前生效</span> ng-if="release.active">当前生效</span>
</div> </div>
<div class="row"> <div class="row" id="{{release.baseInfo.name}}">
<div class="col-md-2 user"> <div class="col-md-2 user">
<img src="../img/user.png" class="i-20"> <img src="../img/user.png" class="i-20">
<span class="info" ng-bind="release.baseInfo.dataChangeCreatedBy"></span> <span class="info" ng-bind="release.baseInfo.dataChangeCreatedBy"></span>
...@@ -50,7 +50,8 @@ ...@@ -50,7 +50,8 @@
<div class="col-md-3 time"> <div class="col-md-3 time">
<img src="../img/title.png" class="i-20"> <img src="../img/title.png" class="i-20">
<span class="info" <span class="info"
ng-bind="release.baseInfo.name"></span> ng-bind="release.baseInfo.name" ng-class="{'highlight':pageContext.scrollTo == release.baseInfo.name}"></span>
</div> </div>
<div class="col-md-4 comment" ng-show="release.baseInfo.comment"> <div class="col-md-4 comment" ng-show="release.baseInfo.comment">
<img src="../img/comment.png" class="i-20"> <img src="../img/comment.png" class="i-20">
......
...@@ -9,7 +9,7 @@ application_module.controller("ConfigNamespaceController", ...@@ -9,7 +9,7 @@ application_module.controller("ConfigNamespaceController",
var namespace_view_type = { var namespace_view_type = {
TEXT: 'text', TEXT: 'text',
TABLE: 'table', TABLE: 'table',
LOG: 'log' HISTORY: 'history'
}; };
var TABLE_VIEW_OPER_TYPE = { var TABLE_VIEW_OPER_TYPE = {
...@@ -39,8 +39,6 @@ application_module.controller("ConfigNamespaceController", ...@@ -39,8 +39,6 @@ application_module.controller("ConfigNamespaceController",
$scope.editItem = editItem; $scope.editItem = editItem;
$scope.cancelEdit = cancelEdit;
$scope.createItem = createItem; $scope.createItem = createItem;
$scope.doItem = doItem; $scope.doItem = doItem;
...@@ -169,10 +167,10 @@ application_module.controller("ConfigNamespaceController", ...@@ -169,10 +167,10 @@ application_module.controller("ConfigNamespaceController",
function preRollback(namespace) { function preRollback(namespace) {
$scope.toRollbackNamespace = namespace; $scope.toRollbackNamespace = namespace;
//load latest two active releases //load latest two active releases
ReleaseService.findActiveRelease($rootScope.pageContext.appId, ReleaseService.findActiveReleases($rootScope.pageContext.appId,
$rootScope.pageContext.env, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName, $rootScope.pageContext.clusterName,
$scope.toRollbackNamespace.baseInfo.namespaceName, 0, 2) $scope.toRollbackNamespace.baseInfo.namespaceName, 0, 2)
.then(function (result) { .then(function (result) {
if (result.length <= 1) { if (result.length <= 1) {
toastr.error("没有可以回滚的发布历史"); toastr.error("没有可以回滚的发布历史");
...@@ -242,28 +240,18 @@ application_module.controller("ConfigNamespaceController", ...@@ -242,28 +240,18 @@ application_module.controller("ConfigNamespaceController",
}); });
} }
var backupItem = {};
//修改配置 //修改配置
function editItem(namespace, item) { function editItem(namespace, item) {
if (!lockCheck(namespace)) { if (!lockCheck(namespace)) {
return; return;
} }
switchTableViewOperType(TABLE_VIEW_OPER_TYPE.UPDATE); switchTableViewOperType(TABLE_VIEW_OPER_TYPE.UPDATE);
$scope.item = item; $scope.item = _.clone(item);
backupItem.value = item.value;
backupItem.comment = item.comment;
toOperationNamespace = namespace; toOperationNamespace = namespace;
$("#itemModal").modal("show"); $("#itemModal").modal("show");
} }
function cancelEdit() {
if($scope.tableViewOperType = TABLE_VIEW_OPER_TYPE.UPDATE){
$scope.item.value = backupItem.value;
$scope.item.comment = backupItem.comment;
}
}
//新增配置 //新增配置
function createItem(namespace) { function createItem(namespace) {
if (!lockCheck(namespace)) { if (!lockCheck(namespace)) {
......
release_history_module.controller("ReleaseHistoryController", release_history_module.controller("ReleaseHistoryController",
['$scope', '$location', '$window', 'toastr', 'AppService', 'AppUtil', ['$scope', '$location', '$anchorScroll', '$window', 'toastr', 'AppService', 'AppUtil',
'ReleaseService', 'ReleaseService',
function ($scope, $location, $window, toastr, AppService, AppUtil, ReleaseService) { function ($scope, $location, $anchorScroll, $window, toastr, AppService, AppUtil, ReleaseService) {
var params = AppUtil.parseParams($location.$$url); var params = AppUtil.parseParams($location.$$url);
$scope.pageContext = { $scope.pageContext = {
appId: params.appid, appId: params.appid,
env: params.env, env: params.env,
clusterName: params.clusterName, clusterName: params.clusterName,
namespaceName: params.namespaceName namespaceName: params.namespaceName,
scrollTo: params.scrollTo
}; };
$scope.page = 0; $scope.page = 0;
...@@ -49,6 +50,12 @@ release_history_module.controller("ReleaseHistoryController", ...@@ -49,6 +50,12 @@ release_history_module.controller("ReleaseHistoryController",
} }
$scope.releases.push(release); $scope.releases.push(release);
}) })
if ($scope.pageContext.scrollTo){
$location.hash($scope.pageContext.scrollTo);
$anchorScroll();
}
}, function (result) { }, function (result) {
toastr.error(AppUtil.errorMsg(result)); toastr.error(AppUtil.errorMsg(result));
}); });
......
appService.service('InstanceService', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('', {}, {
find_instances_by_release: {
method: 'GET',
url: '/envs/:env/instances/by-release'
},
find_instances_by_namespace: {
method: 'GET',
isArray: false,
url: '/envs/:env/instances/by-namespace'
},
find_by_releases_not_in: {
method: 'GET',
isArray: true,
url: '/envs/:env/instances/by-namespace-and-releases-not-in'
},
get_instance_count_by_namespace: {
method: 'GET',
isArray: false,
url: "envs/:env/instances/by-namespace/count"
}
});
return {
findInstancesByRelease: function (env, releaseId, page) {
var d = $q.defer();
resource.find_instances_by_release({
env: env,
releaseId: releaseId,
page: page
},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
findInstancesByNamespace: function (appId, env, clusterName, namespaceName, page) {
var d = $q.defer();
resource.find_instances_by_namespace({
env: env,
appId: appId,
clusterName: clusterName,
namespaceName: namespaceName,
page: page
},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
findByReleasesNotIn: function (appId, env, clusterName, namespaceName, releaseIds) {
var d = $q.defer();
resource.find_by_releases_not_in({
env: env,
appId: appId,
clusterName: clusterName,
namespaceName: namespaceName,
releaseIds: releaseIds
},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
getInstanceCountByNamespace: function (appId, env, clusterName, namespaceName) {
var d = $q.defer();
resource.get_instance_count_by_namespace({
env: env,
appId: appId,
clusterName: clusterName,
namespaceName: namespaceName
},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
...@@ -107,7 +107,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -107,7 +107,7 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return { return {
release: createRelease, release: createRelease,
findAllRelease: findAllReleases, findAllRelease: findAllReleases,
findActiveRelease: findActiveReleases, findActiveReleases: findActiveReleases,
compare: compare, compare: compare,
rollback: rollback rollback: rollback
} }
......
...@@ -33,6 +33,10 @@ table { ...@@ -33,6 +33,10 @@ table {
border-radius: 0px; border-radius: 0px;
} }
.highlight {
background: #ffa;
}
.hide-border-top { .hide-border-top {
border-top: 0px; border-top: 0px;
} }
...@@ -58,6 +62,24 @@ table { ...@@ -58,6 +62,24 @@ table {
width: 15px; width: 15px;
} }
.badge{
padding: 1px 4px;
}
.badge-grey {
background: #777;
color: #fff;
}
.badge-white {
background: #ffffff;
color: #0f0f0f;
}
.panel-default > .panel-heading .badge{
}
.apollo-container { .apollo-container {
min-height: 90%; min-height: 90%;
} }
...@@ -219,12 +241,6 @@ table th { ...@@ -219,12 +241,6 @@ table th {
font-size: 18px; font-size: 18px;
} }
.config-item-container .panel-heading .badge {
padding: 1px 4px;
background: #ffffff;
color: #0f0f0f;
}
.config-item-container .form-control[disabled] { .config-item-container .form-control[disabled] {
background: #ffffff; background: #ffffff;
border: 0px; border: 0px;
...@@ -313,6 +329,16 @@ table th { ...@@ -313,6 +329,16 @@ table th {
} }
.instance-view .btn-primary .badge{
color: #337ab7;
background-color: #fff;
}
.instance-view .btn-default .badge{
background: #777;
color: #fff;
}
.line { .line {
width: 20px; width: 20px;
border: 1px solid #ddd; border: 1px solid #ddd;
...@@ -365,7 +391,6 @@ table th { ...@@ -365,7 +391,6 @@ table th {
padding: 5px 0 5px 50px; padding: 5px 0 5px 50px;
} }
/*搜索框*/ /*搜索框*/
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0; width: 0;
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
</li> </li>
</ul> </ul>
<form class="navbar-form navbar-right form-inline" role="search"> <div class="navbar-form navbar-right form-inline" role="search">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control search-input" placeholder="应用ID/应用名" style="width: 350px" <input type="text" class="form-control search-input" placeholder="应用ID/应用名" style="width: 350px"
ng-model="searchKey" ng-change="changeSearchKey()" ng-focus="changeSearchKey()"> ng-model="searchKey" ng-change="changeSearchKey()" ng-focus="changeSearchKey()">
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
</div> </div>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-default" ng-click="jumpToConfigPage()">Go</button> <button type="button" class="btn btn-default" ng-click="jumpToConfigPage()">Go</button>
</form> </div>
</div> </div>
</div> </div>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
title="{{namespace.comment}}"></b> title="{{namespace.comment}}"></b>
<span class="label label-info no-radius" ng-bind="namespace.format"></span> <span class="label label-info no-radius" ng-bind="namespace.format"></span>
<span class="label label-primary no-radius" ng-show="namespace.itemModifiedCnt > 0">有修改 <span class="label label-primary no-radius" ng-show="namespace.itemModifiedCnt > 0">有修改
<span class="badge label" ng-bind="namespace.itemModifiedCnt"></span></span> <span class="badge label badge-white" ng-bind="namespace.itemModifiedCnt"></span></span>
<span class="label label-warning no-radius" <span class="label label-warning no-radius"
ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span> ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
</div> </div>
...@@ -60,12 +60,10 @@ ...@@ -60,12 +60,10 @@
</div> </div>
</div> </div>
</header> </header>
<!--f1f2f7-->
<header class="panel-heading second-panel-heading"> <header class="panel-heading second-panel-heading">
<div class="row"> <div class="row">
<div class="col-md-5 pull-left"> <div class="col-md-8 pull-left">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li role="presentation" ng-click="switchView(namespace, 'table')" <li role="presentation" ng-click="switchView(namespace, 'table')"
ng-show="namespace.isPropertiesFormat"> ng-show="namespace.isPropertiesFormat">
...@@ -86,9 +84,16 @@ ...@@ -86,9 +84,16 @@
更改历史 更改历史
</a> </a>
</li> </li>
<li role="presentation" ng-click="switchView(namespace, 'instance')">
<a ng-class="{node_active:namespace.viewType == 'instance'}">
<img src="img/machine.png">
实例列表
<span class="badge badge-grey" ng-bind="namespace.instancesCount"></span>
</a>
</li>
</ul> </ul>
</div> </div>
<div class="col-md-7 text-right"> <div class="col-md-4 text-right">
<a data-tooltip="tooltip" data-placement="bottom" title="取消修改" <a data-tooltip="tooltip" data-placement="bottom" title="取消修改"
ng-show="namespace.isTextEditing && namespace.viewType == 'text'" ng-show="namespace.isTextEditing && namespace.viewType == 'text'"
ng-click="toggleTextEditStatus(namespace)"> ng-click="toggleTextEditStatus(namespace)">
...@@ -195,9 +200,8 @@ ...@@ -195,9 +200,8 @@
</table> </table>
</div> </div>
<!--历史修改视图--> <!--history view-->
<div class="J_historyview history-view" ng-show="namespace.viewType == 'history'"> <div class="J_historyview history-view" ng-show="namespace.viewType == 'history'">
<div class="media" ng-repeat="commits in namespace.commits"> <div class="media" ng-repeat="commits in namespace.commits">
<div class="media-body"> <div class="media-body">
<div class="row"> <div class="row">
...@@ -322,4 +326,142 @@ ...@@ -322,4 +326,142 @@
<span class="glyphicon glyphicon-menu-down"></span></button> <span class="glyphicon glyphicon-menu-down"></span></button>
</div> </div>
</div> </div>
<!--instance view-->
<div class="panel panel-default instance-view" ng-show="namespace.viewType == 'instance'">
<div class="panel-heading">
<div class="row text-right" style="padding-right: 15px;">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'latest_release'}"
ng-click="switchInstanceViewType(namespace, 'latest_release')"> 使用最新配置的实例
<span class="badge" ng-bind="namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'not_latest_release'}"
ng-click="switchInstanceViewType(namespace, 'not_latest_release')">使用非最新配置的实例
<span class="badge"
ng-bind="namespace.instancesCount - namespace.latestReleaseInstances.total"></span>
</button>
<button type="button" class="btn btn-default"
ng-class="{'btn-primary':namespace.instanceViewType == 'all'}"
ng-click="switchInstanceViewType(namespace, 'all')">所有实例
<span class="badge" ng-bind="namespace.instancesCount"></span>
</button>
</div>
<button class="btn btn-default btn-sm"
data-tooltip="tooltip" data-placement="bottom" title="刷新列表"
ng-click="refreshInstancesInfo(namespace)">
<img src="../../img/refresh.png"/>
</button>
</div>
</div>
<!--latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'latest_release'">
<div class="panel-default" ng-if="namespace.latestReleaseInstances.total > 0">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&scrollTo={{namespace.latestRelease.name}}">
{{namespace.latestRelease.name}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.latestReleaseInstances.content">
<td width="25%" ng-bind="instance.appId"></td>
<td width="25%" ng-bind="instance.clusterName"></td>
<td width="25%" ng-bind="instance.dataCenter"></td>
<td width="25%" ng-bind="instance.ip"></td>
</tr>
</tbody>
</table>
<div class="row text-center" ng-show="namespace.latestReleaseInstances.content.length < namespace.latestReleaseInstances.total">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--not latest release instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'not_latest_release'">
<div class="panel-default" ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total > 0" ng-repeat="releaseName in namespace.notLatestReleaseNames">
<div class="panel-heading">
<a target="_blank" data-tooltip="tooltip" data-placement="bottom" title="查看配置"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}&scrollTo={{releaseName}}">
{{releaseName}}
</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.notLatestReleaseInstances[releaseName]">
<td width="25%" ng-bind="instance.appId"></td>
<td width="25%" ng-bind="instance.clusterName"></td>
<td width="25%" ng-bind="instance.dataCenter"></td>
<td width="25%" ng-bind="instance.ip"></td>
</tr>
</tbody>
</table>
</div>
<div class="text-center" ng-if="namespace.instancesCount - namespace.latestReleaseInstances.total == 0">
无实例信息
</div>
</div>
<!--all instances-->
<div class="panel-body" ng-show="namespace.instanceViewType == 'all'">
<div class="panel-default" ng-if="namespace.instancesCount > 0">
<table class="table table-bordered table-striped" ng-if="namespace.allInstances">
<thead>
<tr>
<td>App ID</td>
<td>Cluster Name</td>
<td>Data Center</td>
<td>IP</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="instance in namespace.allInstances">
<td width="25%" ng-bind="instance.appId"></td>
<td width="25%" ng-bind="instance.clusterName"></td>
<td width="25%" ng-bind="instance.dataCenter"></td>
<td width="25%" ng-bind="instance.ip"></td>
</tr>
</tbody>
</table>
<div class="row text-center" ng-show="namespace.allInstances.length < namespace.instancesCount">
<button class="btn btn-default" ng-click="loadInstanceInfo(namespace)">加载更多</button>
</div>
</div>
<div class="text-center" ng-if="namespace.instancesCount == 0">
无实例信息
</div>
</div>
</div>
</div> </div>
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