Commit e56f0a7a authored by lepdou's avatar lepdou

rollback

parent 5acb8c76
...@@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.biz.entity.Namespace; ...@@ -6,7 +6,6 @@ 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.ConfigService;
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.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
...@@ -30,9 +29,6 @@ public class ReleaseController { ...@@ -30,9 +29,6 @@ public class ReleaseController {
@Autowired @Autowired
private ReleaseService releaseService; private ReleaseService releaseService;
@Autowired
private ConfigService configService;
@Autowired @Autowired
private NamespaceService namespaceService; private NamespaceService namespaceService;
...@@ -41,7 +37,7 @@ public class ReleaseController { ...@@ -41,7 +37,7 @@ public class ReleaseController {
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR); private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
@RequestMapping("/release/{releaseId}") @RequestMapping("/releases/{releaseId}")
public ReleaseDTO get(@PathVariable("releaseId") long releaseId) { public ReleaseDTO get(@PathVariable("releaseId") long releaseId) {
Release release = releaseService.findOne(releaseId); Release release = releaseService.findOne(releaseId);
if (release == null) { if (release == null) {
...@@ -50,12 +46,31 @@ public class ReleaseController { ...@@ -50,12 +46,31 @@ public class ReleaseController {
return BeanUtils.transfrom(ReleaseDTO.class, release); return BeanUtils.transfrom(ReleaseDTO.class, release);
} }
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseDTO> findAllReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findAllReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
// TODO: 16/7/25 兼容老接口,下版本删除
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases") @RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases")
public List<ReleaseDTO> find(@PathVariable("appId") String appId, public List<ReleaseDTO> findReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName, @PathVariable("namespaceName") String namespaceName,
Pageable page) { Pageable page) {
List<Release> releases = releaseService.findReleases(appId, clusterName, namespaceName, page); List<Release> releases = releaseService.findAllReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases);
}
@RequestMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active")
public List<ReleaseDTO> findActiveReleases(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName,
Pageable page) {
List<Release> releases = releaseService.findActiveReleases(appId, clusterName, namespaceName, page);
return BeanUtils.batchTransform(ReleaseDTO.class, releases); return BeanUtils.batchTransform(ReleaseDTO.class, releases);
} }
...@@ -63,14 +78,8 @@ public class ReleaseController { ...@@ -63,14 +78,8 @@ public class ReleaseController {
public ReleaseDTO getLatest(@PathVariable("appId") String appId, public ReleaseDTO getLatest(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @PathVariable("clusterName") String clusterName,
@PathVariable("namespaceName") String namespaceName) { @PathVariable("namespaceName") String namespaceName) {
Release release = configService.findRelease(appId, clusterName, namespaceName); Release release = releaseService.findLatestActiveRelease(appId, clusterName, namespaceName);
//// TODO: 16/7/22 返回null return BeanUtils.transfrom(ReleaseDTO.class, release);
if (release == null) {
throw new NotFoundException(String.format("latest release not found for %s %s %s", appId,
clusterName, namespaceName));
} else {
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
} }
@RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST) @RequestMapping(path = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", method = RequestMethod.POST)
...@@ -83,14 +92,28 @@ public class ReleaseController { ...@@ -83,14 +92,28 @@ public class ReleaseController {
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.buildRelease(name, comment, namespace, operator);
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName), messageSender.sendMessage(assembleKey(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)
public void rollback(@PathVariable("releaseId") long releaseId,
@RequestParam("operator") String operator) {
Release release = releaseService.rollback(releaseId, operator);
String appId = release.getAppId();
String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName();
//send release message
messageSender.sendMessage(assembleKey(appId, clusterName, namespaceName),
Topics.APOLLO_RELEASE_TOPIC);
}
private String assembleKey(String appId, String cluster, String namespace) { private String assembleKey(String appId, String cluster, String namespace) {
return STRING_JOINER.join(appId, cluster, namespace); return STRING_JOINER.join(appId, cluster, namespace);
} }
......
...@@ -40,6 +40,9 @@ public class Release extends BaseEntity { ...@@ -40,6 +40,9 @@ public class Release extends BaseEntity {
@Column(name = "Comment", nullable = false) @Column(name = "Comment", nullable = false)
private String comment; private String comment;
@Column(name = "IsAbandoned", columnDefinition = "Bit default '0'")
private boolean isAbandoned;
public String getReleaseKey() { public String getReleaseKey() {
return releaseKey; return releaseKey;
} }
...@@ -96,9 +99,17 @@ public class Release extends BaseEntity { ...@@ -96,9 +99,17 @@ public class Release extends BaseEntity {
this.name = name; this.name = name;
} }
public boolean isAbandoned() {
return isAbandoned;
}
public void setAbandoned(boolean abandoned) {
isAbandoned = abandoned;
}
public String toString() { public String toString() {
return toStringHelper().add("name", name).add("appId", appId).add("clusterName", clusterName) return toStringHelper().add("name", name).add("appId", appId).add("clusterName", clusterName)
.add("namespaceName", namespaceName).add("configurations", configurations) .add("namespaceName", namespaceName).add("configurations", configurations)
.add("comment", comment).toString(); .add("comment", comment).add("isAbandoned", isAbandoned).toString();
} }
} }
...@@ -13,8 +13,10 @@ import com.ctrip.framework.apollo.biz.entity.Release; ...@@ -13,8 +13,10 @@ import com.ctrip.framework.apollo.biz.entity.Release;
*/ */
public interface ReleaseRepository extends PagingAndSortingRepository<Release, Long> { public interface ReleaseRepository extends PagingAndSortingRepository<Release, Long> {
Release findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(@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);
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);
} }
package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Config Service
*
* @author Jason Song(song_s@ctrip.com)
*/
@Service
public class ConfigService {
@Autowired
private ReleaseRepository releaseRepository;
public Release findRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(
appId, clusterName, namespaceName);
return release;
}
}
...@@ -9,9 +9,12 @@ import com.ctrip.framework.apollo.biz.entity.Release; ...@@ -9,9 +9,12 @@ import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ItemRepository; import com.ctrip.framework.apollo.biz.repository.ItemRepository;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGenerator; import com.ctrip.framework.apollo.biz.utils.ReleaseKeyGenerator;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import com.ctrip.framework.apollo.core.exception.NotFoundException;
import com.ctrip.framework.apollo.core.utils.StringUtils; import com.ctrip.framework.apollo.core.utils.StringUtils;
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.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;
...@@ -27,19 +30,19 @@ import java.util.Map; ...@@ -27,19 +30,19 @@ import java.util.Map;
*/ */
@Service @Service
public class ReleaseService { public class ReleaseService {
private Gson gson = new Gson(); private Gson gson = new Gson();
@Autowired @Autowired
private ReleaseRepository releaseRepository; private ReleaseRepository releaseRepository;
@Autowired @Autowired
private ItemRepository itemRepository; private ItemRepository itemRepository;
@Autowired @Autowired
private AuditService auditService; private AuditService auditService;
@Autowired @Autowired
private NamespaceLockService namespaceLockService; private NamespaceLockService namespaceLockService;
@Autowired
private NamespaceService namespaceService;
public Release findOne(long releaseId) { public Release findOne(long releaseId) {
...@@ -47,9 +50,30 @@ public class ReleaseService { ...@@ -47,9 +50,30 @@ public class ReleaseService {
return release; return release;
} }
public List<Release> findReleases(String appId, String clusterName, String namespaceName, Pageable page) { public Release findLatestActiveRelease(String appId, String clusterName, String namespaceName) {
Release release = releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
appId, clusterName, namespaceName);
return release;
}
public List<Release> findAllReleases(String appId, String clusterName, String namespaceName, Pageable page) {
List<Release> releases = releaseRepository.findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(appId, List<Release> releases = releaseRepository.findByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(appId,
clusterName, namespaceName, page); clusterName,
namespaceName,
page);
if (releases == null) {
return Collections.emptyList();
}
return releases;
}
public List<Release> findActiveReleases(String appId, String clusterName, String namespaceName, Pageable page) {
List<Release>
releases =
releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
page);
if (releases == null) { if (releases == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
...@@ -57,7 +81,7 @@ public class ReleaseService { ...@@ -57,7 +81,7 @@ public class ReleaseService {
} }
@Transactional @Transactional
public Release buildRelease(String name, String comment, Namespace namespace, String owner) { public Release buildRelease(String name, String comment, Namespace namespace, String operator) {
List<Item> items = itemRepository.findByNamespaceIdOrderByLineNumAsc(namespace.getId()); List<Item> items = itemRepository.findByNamespaceIdOrderByLineNumAsc(namespace.getId());
Map<String, String> configurations = new HashMap<String, String>(); Map<String, String> configurations = new HashMap<String, String>();
...@@ -71,8 +95,8 @@ public class ReleaseService { ...@@ -71,8 +95,8 @@ public class ReleaseService {
Release release = new Release(); Release release = new Release();
release.setReleaseKey(ReleaseKeyGenerator.generateReleaseKey(namespace)); release.setReleaseKey(ReleaseKeyGenerator.generateReleaseKey(namespace));
release.setDataChangeCreatedTime(new Date()); release.setDataChangeCreatedTime(new Date());
release.setDataChangeCreatedBy(owner); release.setDataChangeCreatedBy(operator);
release.setDataChangeLastModifiedBy(owner); release.setDataChangeLastModifiedBy(operator);
release.setName(name); release.setName(name);
release.setComment(comment); release.setComment(comment);
release.setAppId(namespace.getAppId()); release.setAppId(namespace.getAppId());
...@@ -83,9 +107,43 @@ public class ReleaseService { ...@@ -83,9 +107,43 @@ public class ReleaseService {
namespaceLockService.unlock(namespace.getId()); namespaceLockService.unlock(namespace.getId());
auditService.audit(Release.class.getSimpleName(), release.getId(), Audit.OP.INSERT, auditService.audit(Release.class.getSimpleName(), release.getId(), Audit.OP.INSERT,
release.getDataChangeCreatedBy()); release.getDataChangeCreatedBy());
return release; return release;
} }
@Transactional
public Release rollback(long releaseId, String operator) {
Release release = findOne(releaseId);
if (release == null){
throw new NotFoundException("release not found");
}
if (release.isAbandoned()){
throw new BadRequestException("release is not active");
}
String appId = release.getAppId();
String clusterName = release.getClusterName();
String namespaceName = release.getNamespaceName();
Namespace namespace = namespaceService.findOne(appId, clusterName, namespaceName);
if (namespace == null) {
throw new BadRequestException(String.format("namespace not existed. (appId=%s, cluster=%s, namespace=%s)", appId,
clusterName, namespaceName));
}
PageRequest page = new PageRequest(0, 2);
List<Release> twoLatestActiveReleases = findActiveReleases(appId, clusterName, namespaceName, page);
if (twoLatestActiveReleases == null || twoLatestActiveReleases.size() < 2) {
throw new BadRequestException(String.format(
"Can't rollback namespace(appId=%s, clusterName=%s, namespaceName=%s) because only one active release", appId,
clusterName,
namespaceName));
}
release.setAbandoned(true);
release.setDataChangeLastModifiedBy(operator);
return releaseRepository.save(release);
}
} }
...@@ -28,8 +28,10 @@ public class ConfigChangeContentBuilder { ...@@ -28,8 +28,10 @@ public class ConfigChangeContentBuilder {
} }
public ConfigChangeContentBuilder updateItem(Item oldItem, Item newItem) { public ConfigChangeContentBuilder updateItem(Item oldItem, Item newItem) {
ItemPair itemPair = new ItemPair(oldItem, newItem); if (!oldItem.getValue().equals(newItem.getValue())){
updateItems.add(itemPair); ItemPair itemPair = new ItemPair(oldItem, newItem);
updateItems.add(itemPair);
}
return this; return this;
} }
......
package com.ctrip.framework.apollo.biz;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class)
@Rollback
@Transactional
@WebIntegrationTest(randomPort = true)
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
public class AbstractIntegrationTest {
}
package com.ctrip.framework.apollo.biz;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class AbstractUnitTest {
}
...@@ -8,7 +8,6 @@ import com.ctrip.framework.apollo.biz.repository.AppRepositoryTest; ...@@ -8,7 +8,6 @@ import com.ctrip.framework.apollo.biz.repository.AppRepositoryTest;
import com.ctrip.framework.apollo.biz.service.AdminServiceTest; 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.ConfigServiceTest;
import com.ctrip.framework.apollo.biz.service.PrivilegeServiceTest; import com.ctrip.framework.apollo.biz.service.PrivilegeServiceTest;
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;
...@@ -22,7 +21,6 @@ import org.junit.runners.Suite.SuiteClasses; ...@@ -22,7 +21,6 @@ import org.junit.runners.Suite.SuiteClasses;
AppRepositoryTest.class, AppRepositoryTest.class,
AppNamespaceRepositoryTest.class, AppNamespaceRepositoryTest.class,
AdminServiceTest.class, AdminServiceTest.class,
ConfigServiceTest.class,
PrivilegeServiceTest.class, PrivilegeServiceTest.class,
AdminServiceTransactionTest.class, AdminServiceTransactionTest.class,
DatabaseMessageSenderTest.class, DatabaseMessageSenderTest.class,
......
package com.ctrip.framework.apollo.biz.eureka; package com.ctrip.framework.apollo.biz.eureka;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.service.ServerConfigService; import com.ctrip.framework.apollo.biz.service.ServerConfigService;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when; ...@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) public class ApolloEurekaClientConfigTest extends AbstractUnitTest {
public class ApolloEurekaClientConfigTest {
private ApolloEurekaClientConfig eurekaClientConfig; private ApolloEurekaClientConfig eurekaClientConfig;
@Mock @Mock
private ServerConfigService serverConfigService; private ServerConfigService serverConfigService;
......
package com.ctrip.framework.apollo.biz.message; package com.ctrip.framework.apollo.biz.message;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage; import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
...@@ -20,8 +19,7 @@ import static org.mockito.Mockito.verify; ...@@ -20,8 +19,7 @@ import static org.mockito.Mockito.verify;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) public class DatabaseMessageSenderTest extends AbstractUnitTest{
public class DatabaseMessageSenderTest {
private DatabaseMessageSender messageSender; private DatabaseMessageSender messageSender;
@Mock @Mock
private ReleaseMessageRepository releaseMessageRepository; private ReleaseMessageRepository releaseMessageRepository;
......
...@@ -3,14 +3,13 @@ package com.ctrip.framework.apollo.biz.message; ...@@ -3,14 +3,13 @@ package com.ctrip.framework.apollo.biz.message;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ReleaseMessage; import com.ctrip.framework.apollo.biz.entity.ReleaseMessage;
import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseMessageRepository;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -22,8 +21,7 @@ import static org.mockito.Mockito.when; ...@@ -22,8 +21,7 @@ import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) public class ReleaseMessageScannerTest extends AbstractUnitTest {
public class ReleaseMessageScannerTest {
private ReleaseMessageScanner releaseMessageScanner; private ReleaseMessageScanner releaseMessageScanner;
@Mock @Mock
private ReleaseMessageRepository releaseMessageRepository; private ReleaseMessageRepository releaseMessageRepository;
......
package com.ctrip.framework.apollo.biz.repository; package com.ctrip.framework.apollo.biz.repository;
import com.ctrip.framework.apollo.biz.BizTestConfiguration; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@RunWith(SpringJUnit4ClassRunner.class) public class AppNamespaceRepositoryTest extends AbstractIntegrationTest{
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AppNamespaceRepositoryTest {
@Autowired @Autowired
private AppNamespaceRepository repository; private AppNamespaceRepository repository;
......
...@@ -2,22 +2,12 @@ package com.ctrip.framework.apollo.biz.repository; ...@@ -2,22 +2,12 @@ package com.ctrip.framework.apollo.biz.repository;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.biz.BizTestConfiguration;
import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.entity.App;
@RunWith(SpringJUnit4ClassRunner.class) public class AppRepositoryTest extends AbstractIntegrationTest{
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AppRepositoryTest {
@Autowired @Autowired
private AppRepository appRepository; private AppRepository appRepository;
......
...@@ -5,14 +5,9 @@ import java.util.List; ...@@ -5,14 +5,9 @@ import java.util.List;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.BizTestConfiguration; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.entity.App;
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.Cluster;
...@@ -21,11 +16,7 @@ import com.ctrip.framework.apollo.biz.repository.AppRepository; ...@@ -21,11 +16,7 @@ import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.exception.ServiceException; import com.ctrip.framework.apollo.core.exception.ServiceException;
@RunWith(SpringJUnit4ClassRunner.class) public class AdminServiceTest extends AbstractIntegrationTest{
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class AdminServiceTest {
@Autowired @Autowired
private AdminService adminService; private AdminService adminService;
......
...@@ -6,28 +6,19 @@ import org.junit.After; ...@@ -6,28 +6,19 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Commit;
import org.springframework.test.annotation.Rollback; import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.AfterTransaction; import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction; import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.BizTestConfiguration; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepository; import com.ctrip.framework.apollo.biz.repository.AppNamespaceRepository;
import com.ctrip.framework.apollo.biz.repository.AppRepository; import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.biz.repository.ClusterRepository; import com.ctrip.framework.apollo.biz.repository.ClusterRepository;
import com.ctrip.framework.apollo.biz.repository.NamespaceRepository; import com.ctrip.framework.apollo.biz.repository.NamespaceRepository;
@RunWith(SpringJUnit4ClassRunner.class) public class AdminServiceTransactionTest extends AbstractIntegrationTest {
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Commit
public class AdminServiceTransactionTest {
@Autowired @Autowired
AdminService adminService; AdminService adminService;
......
...@@ -3,22 +3,13 @@ package com.ctrip.framework.apollo.biz.service; ...@@ -3,22 +3,13 @@ package com.ctrip.framework.apollo.biz.service;
import java.util.Date; import java.util.Date;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.BizTestConfiguration; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.core.exception.ServiceException; import com.ctrip.framework.apollo.core.exception.ServiceException;
@RunWith(SpringJUnit4ClassRunner.class) public class ClusterServiceTest extends AbstractIntegrationTest {
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class ClusterServiceTest {
@Autowired @Autowired
private AdminService adminService; private AdminService adminService;
......
...@@ -5,24 +5,15 @@ import java.util.List; ...@@ -5,24 +5,15 @@ import java.util.List;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.ctrip.framework.apollo.biz.BizTestConfiguration; import com.ctrip.framework.apollo.biz.AbstractIntegrationTest;
import com.ctrip.framework.apollo.common.entity.App; import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.biz.entity.Cluster; 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.entity.Privilege; import com.ctrip.framework.apollo.biz.entity.Privilege;
@RunWith(SpringJUnit4ClassRunner.class) public class PrivilegeServiceTest extends AbstractIntegrationTest {
@SpringApplicationConfiguration(classes = BizTestConfiguration.class)
@Transactional
@Rollback
public class PrivilegeServiceTest {
@Autowired @Autowired
private AdminService adminService; private AdminService adminService;
......
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
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.repository.ReleaseRepository; import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.domain.PageRequest;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Arrays;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
...@@ -16,22 +21,91 @@ import static org.mockito.Mockito.times; ...@@ -16,22 +21,91 @@ 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;
/** public class ReleaseServiceTest extends AbstractUnitTest {
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigServiceTest {
@Mock @Mock
private ReleaseRepository releaseRepository; private ReleaseRepository releaseRepository;
@Mock
private ConfigService configService; private NamespaceService namespaceService;
@InjectMocks
private ReleaseService releaseService;
private String appId = "appId-test";
private String clusterName = "cluster-test";
private String namespaceName = "namespace-test";
private String user = "user-test";
private Namespace namespace;
private long releaseId = 1;
private Release firstRelease;
private Release secondRelease;
private PageRequest pageRequest;
@Before @Before
public void setUp() throws Exception { public void init() {
configService = new ConfigService(); namespace = new Namespace();
ReflectionTestUtils.setField(configService, "releaseRepository", releaseRepository); namespace.setAppId(appId);
namespace.setClusterName(clusterName);
namespace.setNamespaceName(namespaceName);
firstRelease = new Release();
firstRelease.setId(releaseId);
firstRelease.setAppId(appId);
firstRelease.setClusterName(clusterName);
firstRelease.setNamespaceName(namespaceName);
firstRelease.setAbandoned(false);
secondRelease = new Release();
secondRelease.setAppId(appId);
secondRelease.setClusterName(clusterName);
secondRelease.setNamespaceName(namespaceName);
secondRelease.setAbandoned(false);
pageRequest = new PageRequest(0, 2);
} }
@Test(expected = BadRequestException.class)
public void testNamespaceNotExist() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenThrow(new BadRequestException("xx"));
releaseService.rollback(releaseId, user);
}
@Test(expected = BadRequestException.class)
public void testHasNoRelease() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenReturn(namespace);
when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
pageRequest))
.thenReturn(null);
releaseService.rollback(releaseId, user);
}
@Test
public void testRollback() {
when(releaseRepository.findOne(releaseId)).thenReturn(firstRelease);
when(namespaceService.findOne(appId, clusterName, namespaceName)).thenReturn(namespace);
when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
clusterName,
namespaceName,
pageRequest))
.thenReturn(
Arrays.asList(firstRelease, secondRelease));
releaseService.rollback(releaseId, user);
verify(releaseRepository).save(firstRelease);
Assert.assertEquals(true, firstRelease.isAbandoned());
Assert.assertEquals(user, firstRelease.getDataChangeLastModifiedBy());
}
@Test @Test
public void testFindRelease() throws Exception { public void testFindRelease() throws Exception {
String someAppId = "1"; String someAppId = "1";
...@@ -44,17 +118,19 @@ public class ConfigServiceTest { ...@@ -44,17 +118,19 @@ public class ConfigServiceTest {
Release Release
someRelease = someRelease =
assembleRelease(someReleaseId, someReleaseKey, someAppId, someClusterName, assembleRelease(someReleaseId, someReleaseKey, someAppId, someClusterName,
someNamespaceName, someNamespaceName,
someValidConfiguration); someValidConfiguration);
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId, when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
someClusterName, someNamespaceName)).thenReturn(someRelease); someClusterName,
someNamespaceName))
.thenReturn(someRelease);
Release result = configService.findRelease(someAppId, someClusterName, someNamespaceName); Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);
verify(releaseRepository, times(1)) verify(releaseRepository, times(1))
.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId, someClusterName, .findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId, someClusterName,
someNamespaceName); someNamespaceName);
assertEquals(someAppId, result.getAppId()); assertEquals(someAppId, result.getAppId());
assertEquals(someClusterName, result.getClusterName()); assertEquals(someClusterName, result.getClusterName());
assertEquals(someReleaseId, result.getId()); assertEquals(someReleaseId, result.getId());
...@@ -68,13 +144,15 @@ public class ConfigServiceTest { ...@@ -68,13 +144,15 @@ public class ConfigServiceTest {
String someClusterName = "someClusterName"; String someClusterName = "someClusterName";
String someNamespaceName = "someNamespaceName"; String someNamespaceName = "someNamespaceName";
when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc(someAppId, when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
someClusterName, someNamespaceName)).thenReturn(null); someClusterName,
someNamespaceName))
.thenReturn(null);
Release result = configService.findRelease(someAppId, someClusterName, someNamespaceName); Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);
assertNull(result); assertNull(result);
verify(releaseRepository, times(1)).findFirstByAppIdAndClusterNameAndNamespaceNameOrderByIdDesc( verify(releaseRepository, times(1)).findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
someAppId, someClusterName, someNamespaceName); someAppId, someClusterName, someNamespaceName);
} }
...@@ -91,4 +169,5 @@ public class ConfigServiceTest { ...@@ -91,4 +169,5 @@ public class ConfigServiceTest {
return release; return release;
} }
} }
package com.ctrip.framework.apollo.biz.service; package com.ctrip.framework.apollo.biz.service;
import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.entity.ServerConfig; import com.ctrip.framework.apollo.biz.entity.ServerConfig;
import com.ctrip.framework.apollo.biz.repository.ServerConfigRepository; import com.ctrip.framework.apollo.biz.repository.ServerConfigRepository;
import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.core.ConfigConsts;
...@@ -7,9 +8,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts; ...@@ -7,9 +8,7 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.*; import static org.junit.Assert.*;
...@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when; ...@@ -19,8 +18,7 @@ import static org.mockito.Mockito.when;
/** /**
* @author Jason Song(song_s@ctrip.com) * @author Jason Song(song_s@ctrip.com)
*/ */
@RunWith(MockitoJUnitRunner.class) public class ServerConfigServiceTest extends AbstractUnitTest{
public class ServerConfigServiceTest {
private ServerConfigService serverConfigService; private ServerConfigService serverConfigService;
@Mock @Mock
private ServerConfigRepository serverConfigRepository; private ServerConfigRepository serverConfigRepository;
......
...@@ -8,10 +8,10 @@ import com.google.common.collect.Maps; ...@@ -8,10 +8,10 @@ import com.google.common.collect.Maps;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
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;
import com.ctrip.framework.apollo.biz.service.AppNamespaceService; import com.ctrip.framework.apollo.biz.service.AppNamespaceService;
import com.ctrip.framework.apollo.biz.service.ConfigService;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil; import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
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;
...@@ -39,7 +39,7 @@ import javax.servlet.http.HttpServletResponse; ...@@ -39,7 +39,7 @@ import javax.servlet.http.HttpServletResponse;
@RequestMapping("/configs") @RequestMapping("/configs")
public class ConfigController { public class ConfigController {
@Autowired @Autowired
private ConfigService configService; private ReleaseService releaseService;
@Autowired @Autowired
private AppNamespaceService appNamespaceService; private AppNamespaceService appNamespaceService;
@Autowired @Autowired
...@@ -143,7 +143,7 @@ public class ConfigController { ...@@ -143,7 +143,7 @@ public class ConfigController {
//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, clusterName)) {
Release clusterRelease = Release clusterRelease =
configService.findRelease(appId, clusterName, namespace); releaseService.findLatestActiveRelease(appId, clusterName, namespace);
if (!Objects.isNull(clusterRelease)) { if (!Objects.isNull(clusterRelease)) {
return clusterRelease; return clusterRelease;
...@@ -153,15 +153,15 @@ public class ConfigController { ...@@ -153,15 +153,15 @@ 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, clusterName)) {
Release dataCenterRelease = Release dataCenterRelease =
configService.findRelease(appId, dataCenter, namespace); releaseService.findLatestActiveRelease(appId, dataCenter, namespace);
if (!Objects.isNull(dataCenterRelease)) { if (!Objects.isNull(dataCenterRelease)) {
return dataCenterRelease; return dataCenterRelease;
} }
} }
//fallback to default release //fallback to default release
return configService return releaseService
.findRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace); .findLatestActiveRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
} }
/** /**
......
...@@ -17,6 +17,8 @@ public class ReleaseDTO extends BaseDTO{ ...@@ -17,6 +17,8 @@ public class ReleaseDTO extends BaseDTO{
private String comment; private String comment;
private boolean isAbandoned;
public long getId() { public long getId() {
return id; return id;
} }
...@@ -81,4 +83,11 @@ public class ReleaseDTO extends BaseDTO{ ...@@ -81,4 +83,11 @@ public class ReleaseDTO extends BaseDTO{
this.namespaceName = namespaceName; this.namespaceName = namespaceName;
} }
public boolean isAbandoned() {
return isAbandoned;
}
public void setAbandoned(boolean abandoned) {
isAbandoned = abandoned;
}
} }
...@@ -144,10 +144,24 @@ public class AdminServiceAPI { ...@@ -144,10 +144,24 @@ public class AdminServiceAPI {
@Service @Service
public static class ReleaseAPI extends API { public static class ReleaseAPI extends API {
public List<ReleaseDTO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page, public ReleaseDTO loadRelease(Env env, long releaseId) {
int size) { return restTemplate.get(env, "releases/{releaseId}", ReleaseDTO.class, releaseId);
}
public List<ReleaseDTO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
ReleaseDTO[] releaseDTOs = restTemplate.get(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all?page={page}&size={size}",
ReleaseDTO[].class,
appId, clusterName, namespaceName, page, size);
return Arrays.asList(releaseDTOs);
}
public List<ReleaseDTO> findActiveReleases(String appId, Env env, String clusterName, String namespaceName,
int page,
int size) {
ReleaseDTO[] releaseDTOs = restTemplate.get( ReleaseDTO[] releaseDTOs = restTemplate.get(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}", env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active?page={page}&size={size}",
ReleaseDTO[].class, ReleaseDTO[].class,
appId, clusterName, namespaceName, page, size); appId, clusterName, namespaceName, page, size);
return Arrays.asList(releaseDTOs); return Arrays.asList(releaseDTOs);
...@@ -161,8 +175,8 @@ public class AdminServiceAPI { ...@@ -161,8 +175,8 @@ public class AdminServiceAPI {
return releaseDTO; return releaseDTO;
} }
public ReleaseDTO release(String appId, Env env, String clusterName, String namespace, public ReleaseDTO createRelease(String appId, Env env, String clusterName, String namespace,
String releaseTitle, String comment, String operator) { String releaseTitle, String comment, String operator) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8")); headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
...@@ -177,6 +191,12 @@ public class AdminServiceAPI { ...@@ -177,6 +191,12 @@ public class AdminServiceAPI {
appId, clusterName, namespace); appId, clusterName, namespace);
return response; return response;
} }
public void rollback(Env env, long releaseId, String operator) {
restTemplate.put(env,
"releases/{releaseId}/rollback?operator={operator}",
null, releaseId, operator);
}
} }
@Service @Service
......
...@@ -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 REFRESH_INTERVAL = 5 * 60 * 1000;
private static final long REFRESH_INTERVAL = 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";
......
...@@ -66,6 +66,10 @@ public class RetryableRestTemplate { ...@@ -66,6 +66,10 @@ public class RetryableRestTemplate {
private <T> T execute(HttpMethod method, Env env, String path, Object request, Class<T> responseType, private <T> T execute(HttpMethod method, Env env, String path, Object request, Class<T> responseType,
Object... uriVariables) { Object... uriVariables) {
if (path.startsWith("/")){
path = path.substring(1, path.length());
}
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);
......
...@@ -138,5 +138,4 @@ public class AppController { ...@@ -138,5 +138,4 @@ public class AppController {
return response; return response;
} }
} }
...@@ -4,6 +4,7 @@ import com.ctrip.framework.apollo.common.utils.RequestPrecondition; ...@@ -4,6 +4,7 @@ import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO; import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO;
import com.ctrip.framework.apollo.portal.service.ReleaseService; import com.ctrip.framework.apollo.portal.service.ReleaseService;
...@@ -42,16 +43,46 @@ public class ReleaseController { ...@@ -42,16 +43,46 @@ public class ReleaseController {
} }
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases") @RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/all")
public List<ReleaseVO> findReleases(@PathVariable String appId, public List<ReleaseVO> findAllReleases(@PathVariable String appId,
@PathVariable String env, @PathVariable String clusterName, @PathVariable String env,
@PathVariable String namespaceName, @PathVariable String clusterName,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size){ @PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
RequestPrecondition.checkNumberPositive(size); RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page); RequestPrecondition.checkNumberNotNegative(page);
return releaseService.findReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size); return releaseService.findAllReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}
@RequestMapping(value = "/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/releases/active")
public List<ReleaseDTO> findActiveReleases(@PathVariable String appId,
@PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);
return releaseService.findActiveReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}
@RequestMapping(value = "/envs/{env}/releases/compare")
public ReleaseCompareResult compareRelease(@PathVariable String env,
@RequestParam long firstReleaseId,
@RequestParam long secondReleaseId) {
return releaseService.compare(Env.valueOf(env), firstReleaseId, secondReleaseId);
}
@RequestMapping(path = "/envs/{env}/releases/{releaseId}/rollback", method = RequestMethod.PUT)
public void rollback(@PathVariable String env,
@PathVariable long releaseId) {
releaseService.rollback(Env.valueOf(env), releaseId);
} }
} }
package com.ctrip.framework.apollo.portal.entity.vo;
public class EntityPair<E> {
private E firstEntity;
private E secondEntity;
public EntityPair(E firstEntity, E secondEntity){
this.firstEntity = firstEntity;
this.secondEntity = secondEntity;
}
public E getFirstEntity() {
return firstEntity;
}
public void setFirstEntity(E firstEntity) {
this.firstEntity = firstEntity;
}
public E getSecondEntity() {
return secondEntity;
}
public void setSecondEntity(E secondEntity) {
this.secondEntity = secondEntity;
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
public class KVEntity {
private String key;
private String value;
public KVEntity(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
package com.ctrip.framework.apollo.portal.entity.vo; package com.ctrip.framework.apollo.portal.entity.vo;
import com.ctrip.framework.apollo.core.dto.ItemDTO; import com.ctrip.framework.apollo.core.dto.ItemDTO;
import com.ctrip.framework.apollo.core.dto.NamespaceDTO; import com.ctrip.framework.apollo.core.dto.NamespaceDTO;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import java.util.List; import java.util.List;
public class NamespaceVO { public class NamespaceVO {
private NamespaceDTO namespace; private NamespaceDTO baseInfo;
private int itemModifiedCnt; private int itemModifiedCnt;
private List<ItemVO> items; private List<ItemVO> items;
private String format; private String format;
...@@ -14,12 +13,12 @@ public class NamespaceVO { ...@@ -14,12 +13,12 @@ public class NamespaceVO {
private String parentAppId; private String parentAppId;
public NamespaceDTO getNamespace() { public NamespaceDTO getBaseInfo() {
return namespace; return baseInfo;
} }
public void setNamespace(NamespaceDTO namespace) { public void setBaseInfo(NamespaceDTO baseInfo) {
this.namespace = namespace; this.baseInfo = baseInfo;
} }
public int getItemModifiedCnt() { public int getItemModifiedCnt() {
......
package com.ctrip.framework.apollo.portal.entity.vo;
import java.util.LinkedList;
import java.util.List;
public class ReleaseCompareResult {
private List<EntityPair<KVEntity>> changes = new LinkedList<>();
public void addEntityPair(KVEntity firstEntity, KVEntity secondEntity){
changes.add(new EntityPair<>(firstEntity, secondEntity));
}
public List<EntityPair<KVEntity>> getChanges() {
return changes;
}
public void setChanges(
List<EntityPair<KVEntity>> changes) {
this.changes = changes;
}
}
...@@ -26,29 +26,4 @@ public class ReleaseVO { ...@@ -26,29 +26,4 @@ public class ReleaseVO {
this.items = items; this.items = items;
} }
public static class KVEntity{
String key;
String value;
public KVEntity(String key, String value){
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
} }
...@@ -4,7 +4,6 @@ import com.google.gson.Gson; ...@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.ctrip.framework.apollo.common.entity.AppNamespace; import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.ExceptionUtils;
import com.ctrip.framework.apollo.core.dto.ItemDTO; import com.ctrip.framework.apollo.core.dto.ItemDTO;
import com.ctrip.framework.apollo.core.dto.NamespaceDTO; import com.ctrip.framework.apollo.core.dto.NamespaceDTO;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO; import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
...@@ -20,9 +19,7 @@ import com.dianping.cat.Cat; ...@@ -20,9 +19,7 @@ import com.dianping.cat.Cat;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -92,7 +89,7 @@ public class NamespaceService { ...@@ -92,7 +89,7 @@ public class NamespaceService {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) { private NamespaceVO parseNamespace(String appId, Env env, String clusterName, NamespaceDTO namespace) {
NamespaceVO namespaceVO = new NamespaceVO(); NamespaceVO namespaceVO = new NamespaceVO();
namespaceVO.setNamespace(namespace); namespaceVO.setBaseInfo(namespace);
fillFormatAndIsPublicAndParentAppField(namespaceVO); fillFormatAndIsPublicAndParentAppField(namespaceVO);
...@@ -104,17 +101,9 @@ public class NamespaceService { ...@@ -104,17 +101,9 @@ public class NamespaceService {
//latest Release //latest Release
ReleaseDTO latestRelease = null; ReleaseDTO latestRelease = null;
Map<String, String> releaseItems = new HashMap<>(); Map<String, String> releaseItems = new HashMap<>();
try { latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName); if (latestRelease != null) {
if (latestRelease != null) { releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class);
releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class);
}
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
logger.warn(ExceptionUtils.toString(e));
} else {
throw e;
}
} }
//not Release config items //not Release config items
...@@ -143,7 +132,7 @@ public class NamespaceService { ...@@ -143,7 +132,7 @@ public class NamespaceService {
private void fillFormatAndIsPublicAndParentAppField(NamespaceVO namespace) { private void fillFormatAndIsPublicAndParentAppField(NamespaceVO namespace) {
NamespaceDTO namespaceDTO = namespace.getNamespace(); NamespaceDTO namespaceDTO = namespace.getBaseInfo();
//先从当前appId下面找,包含私有的和公共的 //先从当前appId下面找,包含私有的和公共的
AppNamespace appNamespace = AppNamespace appNamespace =
appNamespaceService.findByAppIdAndName(namespaceDTO.getAppId(), namespaceDTO.getNamespaceName()); appNamespaceService.findByAppIdAndName(namespaceDTO.getAppId(), namespaceDTO.getNamespaceName());
......
...@@ -8,6 +8,8 @@ import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; ...@@ -8,6 +8,8 @@ import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder; import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType; import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel; import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.KVEntity;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO; import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO;
import com.dianping.cat.Cat; import com.dianping.cat.Cat;
...@@ -38,15 +40,16 @@ public class ReleaseService { ...@@ -38,15 +40,16 @@ public class ReleaseService {
String clusterName = model.getClusterName(); String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName(); String namespaceName = model.getNamespaceName();
ReleaseDTO releaseDTO = ReleaseDTO releaseDTO =
releaseAPI.release(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment() releaseAPI
, userInfoHolder.getUser().getUserId()); .createRelease(appId, env, clusterName, namespaceName, model.getReleaseTitle(), model.getReleaseComment()
, userInfoHolder.getUser().getUserId());
Cat.logEvent(CatEventType.RELEASE_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName)); Cat.logEvent(CatEventType.RELEASE_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
return releaseDTO; return releaseDTO;
} }
public List<ReleaseVO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page, public List<ReleaseVO> findAllReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) { int size) {
List<ReleaseDTO> releaseDTOs = releaseAPI.findReleases(appId, env, clusterName, namespaceName, page, size); List<ReleaseDTO> releaseDTOs = releaseAPI.findAllReleases(appId, env, clusterName, namespaceName, page, size);
if (CollectionUtils.isEmpty(releaseDTOs)) { if (CollectionUtils.isEmpty(releaseDTOs)) {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
...@@ -57,10 +60,10 @@ public class ReleaseService { ...@@ -57,10 +60,10 @@ public class ReleaseService {
ReleaseVO release = new ReleaseVO(); ReleaseVO release = new ReleaseVO();
release.setBaseInfo(releaseDTO); release.setBaseInfo(releaseDTO);
Set<ReleaseVO.KVEntity> kvEntities = new LinkedHashSet<>(); Set<KVEntity> kvEntities = new LinkedHashSet<>();
Set<Map.Entry> entries = gson.fromJson(releaseDTO.getConfigurations(), Map.class).entrySet(); Set<Map.Entry> entries = gson.fromJson(releaseDTO.getConfigurations(), Map.class).entrySet();
for (Map.Entry<String, String> entry : entries) { for (Map.Entry<String, String> entry : entries) {
kvEntities.add(new ReleaseVO.KVEntity(entry.getKey(), entry.getValue())); kvEntities.add(new KVEntity(entry.getKey(), entry.getValue()));
} }
release.setItems(kvEntities); release.setItems(kvEntities);
//为了减少数据量 //为了减少数据量
...@@ -70,4 +73,45 @@ public class ReleaseService { ...@@ -70,4 +73,45 @@ public class ReleaseService {
return releases; return releases;
} }
public List<ReleaseDTO> findActiveReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
return releaseAPI.findActiveReleases(appId, env, clusterName, namespaceName, page, size);
}
public void rollback(Env env, long releaseId) {
releaseAPI.rollback(env, releaseId, userInfoHolder.getUser().getUserId());
}
public ReleaseCompareResult compare(Env env, long firstReleaseId, long secondReleaseId) {
ReleaseDTO firstRelease = releaseAPI.loadRelease(env, firstReleaseId);
ReleaseDTO secondRelease = releaseAPI.loadRelease(env, secondReleaseId);
Map<String, String> firstItems = gson.fromJson(firstRelease.getConfigurations(), Map.class);
Map<String, String> secondItems = gson.fromJson(secondRelease.getConfigurations(), Map.class);
ReleaseCompareResult compareResult = new ReleaseCompareResult();
//added and modified in firstRelease
for (Map.Entry<String, String> entry : firstItems.entrySet()) {
String key = entry.getKey();
String firstValue = entry.getValue();
String secondValue = secondItems.get(key);
if (secondValue == null || !firstValue.equals(secondValue)) {
compareResult.addEntityPair(new KVEntity(key, firstValue), new KVEntity(key, secondValue));
}
}
//deleted in firstRelease
for (Map.Entry<String, String> entry : secondItems.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (firstItems.get(key) == null) {
compareResult.addEntityPair(new KVEntity(key, ""), new KVEntity(key, value));
}
}
return compareResult;
}
} }
...@@ -96,7 +96,8 @@ ...@@ -96,7 +96,8 @@
env="pageContext.env" cluster="pageContext.clusterName" env="pageContext.env" cluster="pageContext.clusterName"
pre-release-ns="prepareReleaseNamespace" pre-release-ns="prepareReleaseNamespace"
create-item="createItem" edit-item="editItem" create-item="createItem" edit-item="editItem"
pre-delete-item="preDeleteItem" commit-change="commitChange"></apollonspanel> pre-delete-item="preDeleteItem" commit-change="commitChange"
show-rollback-tips="showRollbackTips"></apollonspanel>
</div> </div>
<!-- delete modal--> <!-- delete modal-->
...@@ -114,7 +115,13 @@ ...@@ -114,7 +115,13 @@
<apolloconfirmdialog apollo-dialog-id="'releaseDenyDialog'" apollo-title="'发布受限'" <apolloconfirmdialog apollo-dialog-id="'releaseDenyDialog'" apollo-title="'发布受限'"
apollo-detail="'您不能发布哟~ 编辑和发布不能为同一个人'" apollo-detail="'您不能发布哟~ 编辑和发布不能为同一个人'"
apollo-show-cancel-btn="false"></apolloconfirmdialog> apollo-show-cancel-btn="false"></apolloconfirmdialog>
<!--create release modal-->
<apolloconfirmdialog apollo-dialog-id="'rollbackTips'" apollo-title="'回滚'"
apollo-detail="'此操作将会回滚到上一个发布版本,且作废当前版本。确定要回滚吗?'"
apollo-show-cancel-btn="true" apollo-confirm="preRollback"></apolloconfirmdialog>
<!--create createRelease modal-->
<form class="modal fade form-horizontal" id="releaseModal" tabindex="-1" role="dialog" <form class="modal fade form-horizontal" id="releaseModal" tabindex="-1" role="dialog"
ng-submit="release()"> ng-submit="release()">
<div class="modal-dialog" role="document" style="width: 960px"> <div class="modal-dialog" role="document" style="width: 960px">
...@@ -308,12 +315,82 @@ ...@@ -308,12 +315,82 @@
<button type="button" class="btn btn-default" data-dismiss="modal">关闭 <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"
ng-show="tableViewOperType != 'retrieve'" ng-disabled="addItemBtnDisabled && tableViewOperType == 'create'">提交 ng-show="tableViewOperType != 'retrieve'"
ng-disabled="addItemBtnDisabled && tableViewOperType == 'create'">提交
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
<!--rollback-->
<form class="modal fade form-horizontal" id="rollbackModal" tabindex="-1" role="dialog"
ng-submit="rollback()">
<div class="modal-dialog" role="document" style="width: 960px">
<div class="modal-content">
<div class="modal-header panel-primary">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title">回滚</h4>
</div>
<div class="modal-body">
<div class="row text-center">
<span style="font-size: 18px;" ng-bind="firstRelease.name"></span>
<span style="font-size: 18px;"> &nbsp;回滚到&nbsp;</span>
<span style="font-size: 18px;" ng-bind="secondRelease.name"></span>
</div>
<div class="form-group" style="margin-top: 15px;">
<label class="col-sm-2 control-label">
Changes:</label>
<div class="col-sm-10" ng-if="releaseCompareResult.length > 0">
<table class="table table-bordered table-striped text-center table-hover">
<thead>
<tr>
<th>
Key
</th>
<th>
回滚前
</th>
<th>
回滚后
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="pair in releaseCompareResult">
<td width="20%" ng-bind="pair.firstEntity.key">
</td>
<td width="40%" ng-bind="pair.firstEntity.value">
</td>
<td width="40%" ng-bind="pair.secondEntity.value">
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-sm-5">
<span ng-if="releaseCompareResult.length == 0">
配置没有变化
</span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
<button type="submit" class="btn btn-danger" ng-if="releaseCompareResult.length > 0"
ng-disabled="rollbackBtnDisabled">回滚
</button>
</div>
</div>
</div>
</form>
</div> </div>
</div> </div>
......
...@@ -31,10 +31,16 @@ ...@@ -31,10 +31,16 @@
<div class="media-left media-middle badge-{{$index % 4}}"> <div class="media-left media-middle badge-{{$index % 4}}">
</div> </div>
<div class="media-body"> <div class="media-body">
<div class="row text-right">
<span class="label label-info no-radius"
ng-show="release.baseInfo.isAbandoned">已废弃</span>
<span class="label label-primary no-radius"
ng-if="release.active">当前生效</span>
</div>
<div class="row"> <div class="row">
<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>
</div> </div>
<div class="col-md-3 time"> <div class="col-md-3 time">
<img src="../img/time.png" class="i-20"> <img src="../img/time.png" class="i-20">
...@@ -62,7 +68,8 @@ ...@@ -62,7 +68,8 @@
</td> </td>
</tr> </tr>
</table> </table>
<textarea class="form-control no-radius" rows="15" ng-show="isTextFile" ng-bind="release.items[0].value" disabled> <textarea class="form-control no-radius" rows="15" ng-show="isTextFile"
ng-bind="release.items[0].value" disabled>
</textarea> </textarea>
</div> </div>
......
...@@ -27,18 +27,18 @@ index_module.controller('IndexController', ['$scope', '$window', 'toastr', 'AppS ...@@ -27,18 +27,18 @@ index_module.controller('IndexController', ['$scope', '$window', 'toastr', 'AppS
}, function (result) { }, function (result) {
toastr.error(AppUtil.errorMsg(result), "load apps error"); toastr.error(AppUtil.errorMsg(result), "load apps error");
}); });
}; }
$scope.search = function () { $scope.search = function () {
var key = $scope.searchKey; var key = $scope.searchKey.toLocaleLowerCase();
if (key == '') { if (key == '') {
$scope.apps = apps; $scope.apps = apps;
return; return;
} }
var result = []; var result = [];
apps.forEach(function (item) { apps.forEach(function (item) {
if (item.appId.indexOf(key) >= 0 || item.name.indexOf(key) >= 0) { if (item.appId.toLocaleLowerCase().indexOf(key) >= 0 ||
item.name.toLocaleLowerCase().indexOf(key) >= 0) {
result.push(item); result.push(item);
} }
}); });
......
...@@ -14,29 +14,38 @@ release_history_module.controller("ReleaseHistoryController", ...@@ -14,29 +14,38 @@ release_history_module.controller("ReleaseHistoryController",
$scope.page = 0; $scope.page = 0;
$scope.releases = []; $scope.releases = [];
$scope.hasLoadAll = false; $scope.hasLoadAll = false;
$scope.findReleases = findReleases; $scope.findReleases = findReleases;
$scope.loadMore = loadMore; $scope.loadMore = loadMore;
findReleases($scope.page); findReleases($scope.page);
var hasFindActiveRelease = false;
function findReleases(page) { function findReleases(page) {
ReleaseService.findRelease($scope.pageContext.appId, ReleaseService.findAllRelease($scope.pageContext.appId,
$scope.pageContext.env, $scope.pageContext.env,
$scope.pageContext.clusterName, $scope.pageContext.clusterName,
$scope.pageContext.namespaceName, $scope.pageContext.namespaceName,
page) page)
.then(function (result) { .then(function (result) {
if (!result || result.length == 0){ if (!result || result.length == 0) {
$scope.hasLoadAll = true; $scope.hasLoadAll = true;
return; return;
} }
var hasParseNamepaceType = false; var hasParseNamespaceType = false;
result.forEach(function (release) { result.forEach(function (release) {
if (!hasParseNamepaceType){ if (!hasParseNamespaceType) {
$scope.isTextFile = /\.(json|yaml|yml|xml)$/gi.test(release.baseInfo.namespaceName); $scope.isTextFile =
hasParseNamepaceType = true; /\.(json|yaml|yml|xml)$/gi.test(
release.baseInfo.namespaceName);
hasParseNamespaceType = true;
}
if (!hasFindActiveRelease && !release.baseInfo.isAbandoned) {
release.active = true;
hasFindActiveRelease = true;
} }
$scope.releases.push(release); $scope.releases.push(release);
}) })
......
...@@ -31,8 +31,10 @@ directive_module.directive('apollonav', function ($compile, $window, toastr, App ...@@ -31,8 +31,10 @@ directive_module.directive('apollonav', function ($compile, $window, toastr, App
scope.changeSearchKey = function () { scope.changeSearchKey = function () {
scope.copyedApps = []; scope.copyedApps = [];
var searchKey = scope.searchKey.toLocaleLowerCase();
scope.sourceApps.forEach(function (app) { scope.sourceApps.forEach(function (app) {
if (app.name.indexOf(scope.searchKey) > -1 || app.appId.indexOf(scope.searchKey) > -1) { if (app.name.toLocaleLowerCase().indexOf(searchKey) > -1
|| app.appId.toLocaleLowerCase().indexOf(searchKey) > -1) {
scope.copyedApps.push(app); scope.copyedApps.push(app);
} }
}); });
......
...@@ -12,6 +12,7 @@ directive_module.directive('apollonspanel', ...@@ -12,6 +12,7 @@ directive_module.directive('apollonspanel',
env: '=', env: '=',
cluster: '=', cluster: '=',
preReleaseNs: '=', preReleaseNs: '=',
showRollbackTips: '=',
createItem: '=', createItem: '=',
editItem: '=', editItem: '=',
preDeleteItem: '=', preDeleteItem: '=',
...@@ -74,7 +75,7 @@ directive_module.directive('apollonspanel', ...@@ -74,7 +75,7 @@ directive_module.directive('apollonspanel',
CommitService.find_commits(scope.appId, CommitService.find_commits(scope.appId,
scope.env, scope.env,
scope.cluster, scope.cluster,
namespace.namespace.namespaceName, namespace.baseInfo.namespaceName,
namespace.commitPage) namespace.commitPage)
.then(function (result) { .then(function (result) {
if (result.length == 0) { if (result.length == 0) {
...@@ -116,7 +117,7 @@ directive_module.directive('apollonspanel', ...@@ -116,7 +117,7 @@ directive_module.directive('apollonspanel',
"config/sync.html?#/appid=" + scope.appId + "&env=" "config/sync.html?#/appid=" + scope.appId + "&env="
+ scope.env + "&clusterName=" + scope.env + "&clusterName="
+ scope.cluster + scope.cluster
+ "&namespaceName=" + namespace.namespace.namespaceName; + "&namespaceName=" + namespace.baseInfo.namespaceName;
} }
function modifyByText(namespace) { function modifyByText(namespace) {
...@@ -139,7 +140,7 @@ directive_module.directive('apollonspanel', ...@@ -139,7 +140,7 @@ directive_module.directive('apollonspanel',
//namespace view name hide suffix //namespace view name hide suffix
namespace.viewName = namespace.viewName =
namespace.namespace.namespaceName.replace(".xml", "").replace( namespace.baseInfo.namespaceName.replace(".xml", "").replace(
".properties", ""); ".properties", "");
if (!viewType) { if (!viewType) {
...@@ -155,7 +156,7 @@ directive_module.directive('apollonspanel', ...@@ -155,7 +156,7 @@ directive_module.directive('apollonspanel',
//permission //permission
PermissionService.has_modify_namespace_permission( PermissionService.has_modify_namespace_permission(
scope.appId, scope.appId,
namespace.namespace.namespaceName) namespace.baseInfo.namespaceName)
.then(function (result) { .then(function (result) {
namespace.hasModifyPermission = result.hasPermission; namespace.hasModifyPermission = result.hasPermission;
}, function (result) { }, function (result) {
...@@ -164,7 +165,7 @@ directive_module.directive('apollonspanel', ...@@ -164,7 +165,7 @@ directive_module.directive('apollonspanel',
PermissionService.has_release_namespace_permission( PermissionService.has_release_namespace_permission(
scope.appId, scope.appId,
namespace.namespace.namespaceName) namespace.baseInfo.namespaceName)
.then(function (result) { .then(function (result) {
namespace.hasReleasePermission = result.hasPermission; namespace.hasReleasePermission = result.hasPermission;
}, function (result) { }, function (result) {
...@@ -175,7 +176,7 @@ directive_module.directive('apollonspanel', ...@@ -175,7 +176,7 @@ directive_module.directive('apollonspanel',
NamespaceLockService.get_namespace_lock( NamespaceLockService.get_namespace_lock(
scope.appId, scope.env, scope.appId, scope.env,
scope.cluster, scope.cluster,
namespace.namespace.namespaceName) namespace.baseInfo.namespaceName)
.then(function (result) { .then(function (result) {
if (result.dataChangeCreatedBy) { if (result.dataChangeCreatedBy) {
namespace.lockOwner = result.dataChangeCreatedBy; namespace.lockOwner = result.dataChangeCreatedBy;
......
appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q) { appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q) {
var resource = $resource('', {}, { var resource = $resource('', {}, {
find_releases: { find_all_releases: {
method: 'GET', method: 'GET',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases', url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases/all',
isArray: true isArray: true
}, },
find_active_releases: {
method: 'GET',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/releases/active',
isArray: true
},
compare: {
method: 'GET',
url: '/envs/:env/releases/compare'
},
release: { release: {
method: 'POST', method: 'POST',
url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/release' url: '/apps/:appId/envs/:env/clusters/:clusterName/namespaces/:namespaceName/release'
},
rollback: {
method: 'PUT',
url: "envs/:env/releases/:releaseId/rollback"
} }
}); });
...@@ -29,15 +42,15 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -29,15 +42,15 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return d.promise; return d.promise;
} }
function findReleases(appId, env, clusterName, namespaceName, page) { function findAllReleases(appId, env, clusterName, namespaceName, page) {
var d = $q.defer(); var d = $q.defer();
resource.find_releases({ resource.find_all_releases({
appId: appId, appId: appId,
env: env, env: env,
clusterName: clusterName, clusterName: clusterName,
namespaceName: namespaceName, namespaceName: namespaceName,
page: page page: page
}, function (result) { }, function (result) {
d.resolve(result); d.resolve(result);
}, function (result) { }, function (result) {
d.reject(result); d.reject(result);
...@@ -45,8 +58,57 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q ...@@ -45,8 +58,57 @@ appService.service('ReleaseService', ['$resource', '$q', function ($resource, $q
return d.promise; return d.promise;
} }
function findActiveReleases(appId, env, clusterName, namespaceName, page, size) {
var d = $q.defer();
resource.find_active_releases({
appId: appId,
env: env,
clusterName: clusterName,
namespaceName: namespaceName,
page: page,
size: size
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
function compare(env, firstReleaseId, secondReleaseId) {
var d = $q.defer();
resource.compare({
env: env,
firstReleaseId: firstReleaseId,
secondReleaseId: secondReleaseId
}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
function rollback(env, releaseId) {
var d = $q.defer();
resource.rollback({
env: env,
releaseId: releaseId
}, {}, function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
}
);
return d.promise;
}
return { return {
release: createRelease, release: createRelease,
findRelease: findReleases findAllRelease: findAllReleases,
findActiveRelease: findActiveReleases,
compare: compare,
rollback: rollback
} }
}]); }]);
...@@ -448,3 +448,7 @@ table th { ...@@ -448,3 +448,7 @@ table th {
.release-history .badge-3{ .release-history .badge-3{
background: #3478a8; background: #3478a8;
} }
.release-history .label{
margin-right: 15px;
}
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<div class="row namespace-attribute-panel" ng-if="namespace.isPublic"> <div class="row namespace-attribute-panel" ng-if="namespace.isPublic">
<div class="text-center namespace-attribute-public" data-tooltip="tooltip" data-placement="bottom" <div class="text-center namespace-attribute-public" data-tooltip="tooltip" data-placement="bottom"
title="点击跳转到公共Namespace" ng-click="goToParentAppConfigPage(namespace)"> title="点击跳转到公共Namespace" ng-click="goToParentAppConfigPage(namespace)">
公共 <span ng-show="namespace.parentAppId == namespace.baseInfo.appId">公共</span>
<span ng-show="namespace.parentAppId != namespace.baseInfo.appId">关联</span>
</div> </div>
</div> </div>
<header class="panel-heading"> <header class="panel-heading">
...@@ -10,10 +11,11 @@ ...@@ -10,10 +11,11 @@
<div class="col-md-6"> <div class="col-md-6">
<b ng-bind="namespace.viewName" style="font-size: 20px;"></b> <b ng-bind="namespace.viewName" style="font-size: 20px;"></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" ng-bind="namespace.itemModifiedCnt"></span></span>
<span class="label label-warning no-radius" ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span> <span class="label label-warning no-radius"
ng-show="namespace.lockOwner">当前修改者:{{namespace.lockOwner}}</span>
</div> </div>
<div class="col-md-6 text-right"> <div class="col-md-6 text-right">
<button type="button" <button type="button"
...@@ -26,18 +28,19 @@ ...@@ -26,18 +28,19 @@
</button> </button>
<button type="button" <button type="button"
class="btn btn-default btn-sm J_tableview_btn" disabled class="btn btn-default btn-sm J_tableview_btn"
ng-show="namespace.hasReleasePermission"> ng-show="namespace.hasReleasePermission"
ng-click="showRollbackTips(namespace)">
<img src="img/rollback.png"> <img src="img/rollback.png">
回滚 回滚
</button> </button>
<a type="button" class="btn btn-default btn-sm J_tableview_btn" <a type="button" class="btn btn-default btn-sm J_tableview_btn"
href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.namespace.namespaceName}}"> href="/config/history.html?#/appid={{appId}}&env={{env}}&clusterName={{cluster}}&namespaceName={{namespace.baseInfo.namespaceName}}">
<img src="img/release-history.png"> <img src="img/release-history.png">
发布历史 发布历史
</a> </a>
<a type="button" class="btn btn-default btn-sm J_tableview_btn" <a type="button" class="btn btn-default btn-sm J_tableview_btn"
href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.namespace.namespaceName}}" href="/namespace/role.html?#/appid={{appId}}&namespaceName={{namespace.baseInfo.namespaceName}}"
ng-show="hasAssignUserPermission"> ng-show="hasAssignUserPermission">
<img src="img/assign.png"> <img src="img/assign.png">
授权 授权
......
...@@ -8,8 +8,6 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat; ...@@ -8,8 +8,6 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env; import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI; import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO; import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO;
import com.ctrip.framework.apollo.portal.service.AppNamespaceService;
import com.ctrip.framework.apollo.portal.service.NamespaceService;
import com.ctrip.framework.apollo.portal.service.txtresolver.PropertyResolver; import com.ctrip.framework.apollo.portal.service.txtresolver.PropertyResolver;
import org.junit.Before; import org.junit.Before;
...@@ -94,9 +92,9 @@ public class NamespaceServiceTest { ...@@ -94,9 +92,9 @@ public class NamespaceServiceTest {
assertEquals(4, namespaceVO.getItems().size()); assertEquals(4, namespaceVO.getItems().size());
assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey()); assertEquals("a", namespaceVO.getItems().get(0).getItem().getKey());
assertEquals(2, namespaceVO.getItemModifiedCnt()); assertEquals(2, namespaceVO.getItemModifiedCnt());
assertEquals(appId, namespaceVO.getNamespace().getAppId()); assertEquals(appId, namespaceVO.getBaseInfo().getAppId());
assertEquals(clusterName, namespaceVO.getNamespace().getClusterName()); assertEquals(clusterName, namespaceVO.getBaseInfo().getClusterName());
assertEquals(namespaceName, namespaceVO.getNamespace().getNamespaceName()); assertEquals(namespaceName, namespaceVO.getBaseInfo().getNamespaceName());
} }
......
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