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

Merge pull request #335 from lepdou/cat_log

cat log & 按钮放重复点击 & call admin server retry
......@@ -65,8 +65,7 @@ public class ReleaseController {
@PathVariable("namespaceName") String namespaceName) {
Release release = configService.findRelease(appId, clusterName, namespaceName);
if (release == null) {
throw new NotFoundException(String.format("latest release not found for %s %s %s", appId,
clusterName, namespaceName));
return null;
} else {
return BeanUtils.transfrom(ReleaseDTO.class, release);
}
......
......@@ -46,10 +46,12 @@ public class ClusterService {
}
List<Cluster> clusters = clusterRepository.findByAppId(appId);
Collections.sort(clusters);
if (clusters == null) {
return Collections.emptyList();
}
Collections.sort(clusters);
return clusters;
}
......
package com.ctrip.framework.apollo.biz.aop;
package com.ctrip.framework.apollo.common.aop;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
......@@ -10,9 +10,6 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Aspect
@Component
public class RepositoryAspect {
......
......@@ -31,21 +31,25 @@ public class MachineUtil {
try {
StringBuilder sb = new StringBuilder();
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
while (e.hasMoreElements()) {
NetworkInterface ni = e.nextElement();
sb.append(ni.toString());
byte[] mac = ni.getHardwareAddress();
if (mac != null) {
ByteBuffer bb = ByteBuffer.wrap(mac);
try {
sb.append(bb.getChar());
sb.append(bb.getChar());
sb.append(bb.getChar());
} catch (BufferUnderflowException shortHardwareAddressException) { //NOPMD
// mac with less than 6 bytes. continue
if (e != null){
while (e.hasMoreElements()) {
NetworkInterface ni = e.nextElement();
sb.append(ni.toString());
byte[] mac = ni.getHardwareAddress();
if (mac != null) {
ByteBuffer bb = ByteBuffer.wrap(mac);
try {
sb.append(bb.getChar());
sb.append(bb.getChar());
sb.append(bb.getChar());
} catch (BufferUnderflowException shortHardwareAddressException) { //NOPMD
// mac with less than 6 bytes. continue
}
}
}
}
machinePiece = sb.toString().hashCode();
} catch (Throwable ex) {
// exception sometimes happens with IBM JVM, use random
......
package com.ctrip.framework.apollo.portal;
import com.google.common.base.Strings;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
......@@ -10,7 +9,6 @@ import com.ctrip.framework.apollo.portal.service.ServerConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
......@@ -33,9 +31,8 @@ public class PortalSettings {
private Logger logger = LoggerFactory.getLogger(PortalSettings.class);
private static final int HEALTH_CHECK_INTERVAL = 5000;
private static final int HEALTH_CHECK_INTERVAL = 10 * 1000;
private List<String> allStrEnvs;
@Autowired
ApplicationContext applicationContext;
......@@ -43,9 +40,7 @@ public class PortalSettings {
@Autowired
private ServerConfigService serverConfigService;
private List<Env> allEnvs = new ArrayList<Env>();
private List<Env> activeEnvs;
private List<Env> allEnvs = new ArrayList<>();
//mark env up or down
private Map<Env, Boolean> envStatusMark = new ConcurrentHashMap<>();
......@@ -54,19 +49,16 @@ public class PortalSettings {
@PostConstruct
private void postConstruct() {
//初始化portal支持操作的环境集合,线上的portal可能支持所有的环境操作,而线下环境则支持一部分.
// 每个环境的portal支持哪些环境配置在数据库里
String serverConfig = serverConfigService.getValue("apollo.portal.envs");
if (!Strings.isNullOrEmpty(serverConfig)){
String[] configedEnvs = serverConfig.split(",");
allStrEnvs = Arrays.asList(configedEnvs);
}
String serverConfig = serverConfigService.getValue("apollo.portal.envs", "FAT,UAT,PRO");
String[] configedEnvs = serverConfig.split(",");
List<String> allStrEnvs = Arrays.asList(configedEnvs);
for (String e : allStrEnvs) {
allEnvs.add(Env.valueOf(e.toUpperCase()));
}
for (Env env : allEnvs) {
envStatusMark.put(env, true);
}
......@@ -79,6 +71,10 @@ public class PortalSettings {
}
public List<Env> getAllEnvs(){
return allEnvs;
}
public List<Env> getActiveEnvs() {
List<Env> activeEnvs = new LinkedList<>();
for (Env env : allEnvs) {
......@@ -86,15 +82,9 @@ public class PortalSettings {
activeEnvs.add(env);
}
}
this.activeEnvs = activeEnvs;
return activeEnvs;
}
public Env getFirstAliveEnv() {
return activeEnvs.get(0);
}
class HealthCheckTask implements Runnable {
private static final int ENV_DIED_THREADHOLD = 2;
......
package com.ctrip.framework.apollo.portal.api;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.service.ServiceLocator;
public class API {
@Autowired
protected ServiceLocator serviceLocator;
@Autowired
private RestTemplateFactory restTemplateFactory;
protected RestTemplate restTemplate;
@PostConstruct
private void postConstruct() {
restTemplate = restTemplateFactory.getObject();
}
protected RetryableRestTemplate restTemplate;
public String getAdminServiceHost(Env env) {
return serviceLocator.getServiceAddress(env).getHomepageUrl();
}
}
......@@ -16,7 +16,6 @@ import org.springframework.boot.actuate.health.Health;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
......@@ -32,26 +31,19 @@ public class AdminServiceAPI {
public static class HealthAPI extends API {
public Health health(Env env) {
return restTemplate.getForObject(getAdminServiceHost(env) + "/health", Health.class);
return restTemplate.get(env, "/health", Health.class);
}
}
@Service
public static class AppAPI extends API {
public List<AppDTO> findApps(Env env) {
AppDTO[] appDTOs =
restTemplate.getForObject("{host}/apps", AppDTO[].class, getAdminServiceHost(env));
return Arrays.asList(appDTOs);
}
public AppDTO loadApp(Env env, String appId) {
return restTemplate.getForObject("{host}/apps/{appId}", AppDTO.class, getAdminServiceHost(env), appId);
return restTemplate.get(env, "apps/{appId}", AppDTO.class, appId);
}
public AppDTO createApp(Env env, AppDTO app) {
return restTemplate.postForEntity("{host}/apps", app, AppDTO.class, getAdminServiceHost(env))
.getBody();
return restTemplate.post(env, "apps", app, AppDTO.class);
}
}
......@@ -60,31 +52,30 @@ public class AdminServiceAPI {
public static class NamespaceAPI extends API {
public List<NamespaceDTO> findNamespaceByCluster(String appId, Env env, String clusterName) {
NamespaceDTO[] namespaceDTOs = restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces",
NamespaceDTO[].class, getAdminServiceHost(env), appId,
clusterName);
NamespaceDTO[] namespaceDTOs = restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces",
NamespaceDTO[].class, appId,
clusterName);
return Arrays.asList(namespaceDTOs);
}
public NamespaceDTO loadNamespace(String appId, Env env, String clusterName,
String namespaceName) {
NamespaceDTO
dto =
restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/" + namespaceName,
NamespaceDTO.class, getAdminServiceHost(env), appId, clusterName);
NamespaceDTO dto =
restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}",
NamespaceDTO.class, appId, clusterName, namespaceName);
return dto;
}
public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) {
return restTemplate
.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces", namespace, NamespaceDTO.class,
getAdminServiceHost(env), namespace.getAppId(), namespace.getClusterName()).getBody();
.post(env, "apps/{appId}/clusters/{clusterName}/namespaces", namespace, NamespaceDTO.class,
namespace.getAppId(), namespace.getClusterName());
}
public AppNamespaceDTO createAppNamespace(Env env, AppNamespaceDTO appNamespace) {
return restTemplate.postForEntity("{host}/apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class,
getAdminServiceHost(env), appNamespace.getAppId()).getBody();
return restTemplate
.post(env, "apps/{appId}/appnamespaces", appNamespace, AppNamespaceDTO.class, appNamespace.getAppId());
}
}
......@@ -94,34 +85,32 @@ public class AdminServiceAPI {
public List<ItemDTO> findItems(String appId, Env env, String clusterName, String namespaceName) {
ItemDTO[] itemDTOs =
restTemplate
.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items",
ItemDTO[].class,
getAdminServiceHost(env), appId, clusterName, namespaceName);
restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items",
ItemDTO[].class, appId, clusterName, namespaceName);
return Arrays.asList(itemDTOs);
}
public void updateItemsByChangeSet(String appId, Env env, String clusterName, String namespace,
ItemChangeSets changeSets) {
restTemplate.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/itemset",
changeSets, Void.class, getAdminServiceHost(env), appId, clusterName, namespace);
restTemplate.post(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/itemset",
changeSets, Void.class, appId, clusterName, namespace);
}
public void updateItem(String appId, Env env, String clusterName, String namespace, long itemId, ItemDTO item) {
restTemplate.put("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}",
item, getAdminServiceHost(env), appId, clusterName, namespace, itemId);
restTemplate.put(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items/{itemId}",
item, appId, clusterName, namespace, itemId);
}
public ItemDTO createItem(String appId, Env env, String clusterName, String namespace, ItemDTO item) {
return restTemplate.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items",
item, ItemDTO.class, getAdminServiceHost(env), appId, clusterName, namespace)
.getBody();
return restTemplate.post(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items",
item, ItemDTO.class, appId, clusterName, namespace)
;
}
public void deleteItem(Env env, long itemId, String operator) {
restTemplate.delete("{host}/items/{itemId}?operator={operator}", getAdminServiceHost(env), itemId, operator);
restTemplate.delete(env, "items/{itemId}?operator={operator}", itemId, operator);
}
}
......@@ -129,64 +118,64 @@ public class AdminServiceAPI {
public static class ClusterAPI extends API {
public List<ClusterDTO> findClustersByApp(String appId, Env env) {
ClusterDTO[] clusterDTOs = restTemplate.getForObject("{host}/apps/{appId}/clusters", ClusterDTO[].class,
getAdminServiceHost(env), appId);
ClusterDTO[] clusterDTOs = restTemplate.get(env, "apps/{appId}/clusters", ClusterDTO[].class,
appId);
return Arrays.asList(clusterDTOs);
}
public ClusterDTO loadCluster(String appId, Env env, String clusterName) {
return restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}", ClusterDTO.class,
getAdminServiceHost(env), appId, clusterName);
return restTemplate.get(env, "apps/{appId}/clusters/{clusterName}", ClusterDTO.class,
appId, clusterName);
}
public boolean isClusterUnique(String appId, Env env, String clusterName) {
return restTemplate
.getForObject("{host}/apps/{appId}/cluster/{clusterName}/unique", Boolean.class, getAdminServiceHost(env),
appId, clusterName);
.get(env, "apps/{appId}/cluster/{clusterName}/unique", Boolean.class,
appId, clusterName);
}
public ClusterDTO create(Env env, ClusterDTO cluster) {
return restTemplate.postForObject("{host}/apps/{appId}/clusters", cluster, ClusterDTO.class,
getAdminServiceHost(env), cluster.getAppId());
return restTemplate.post(env, "apps/{appId}/clusters", cluster, ClusterDTO.class,
cluster.getAppId());
}
}
@Service
public static class ReleaseAPI extends API {
public List<ReleaseDTO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page, int size){
ReleaseDTO[] releaseDTOs = restTemplate.getForObject(
"{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}",
public List<ReleaseDTO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
ReleaseDTO[] releaseDTOs = restTemplate.get(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases?page={page}&size={size}",
ReleaseDTO[].class,
getAdminServiceHost(env), appId, clusterName, namespaceName, page, size);
appId, clusterName, namespaceName, page, size);
return Arrays.asList(releaseDTOs);
}
public ReleaseDTO loadLatestRelease(String appId, Env env, String clusterName,
String namespace) {
ReleaseDTO releaseDTO = restTemplate
.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest",
ReleaseDTO.class, getAdminServiceHost(env), appId, clusterName, namespace);
.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases/latest",
ReleaseDTO.class, appId, clusterName, namespace);
return releaseDTO;
}
public ReleaseDTO release(String appId, Env env, String clusterName, String namespace,
String releaseBy, String comment, String operator) {
String releaseTitle, String comment, String operator) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8"));
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("name", releaseBy);
parameters.add("name", releaseTitle);
parameters.add("comment", comment);
parameters.add("operator", operator);
HttpEntity<MultiValueMap<String, String>> entity =
new HttpEntity<MultiValueMap<String, String>>(parameters, headers);
ResponseEntity<ReleaseDTO> response =
restTemplate
.postForEntity("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity,
ReleaseDTO.class,
getAdminServiceHost(env), appId, clusterName, namespace);
return response.getBody();
new HttpEntity<>(parameters, headers);
ReleaseDTO response = restTemplate.post(
env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/releases", entity,
ReleaseDTO.class,
appId, clusterName, namespace);
return response;
}
}
......@@ -195,10 +184,10 @@ public class AdminServiceAPI {
public List<CommitDTO> find(String appId, Env env, String clusterName, String namespaceName, int page, int size) {
CommitDTO[] commitDTOs = restTemplate.getForObject(
"{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/commit?page={page}&size={size}",
CommitDTO[].class,
getAdminServiceHost(env), appId, clusterName, namespaceName, page, size);
CommitDTO[] commitDTOs = restTemplate.get(env,
"apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/commit?page={page}&size={size}",
CommitDTO[].class,
appId, clusterName, namespaceName, page, size);
return Arrays.asList(commitDTOs);
}
......@@ -208,9 +197,9 @@ public class AdminServiceAPI {
public static class NamespaceLockAPI extends API {
public NamespaceLockDTO getNamespaceLockOwner(String appId, Env env, String clusterName, String namespaceName) {
return restTemplate.getForObject("{host}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock",
NamespaceLockDTO.class,
getAdminServiceHost(env), appId, clusterName, namespaceName);
return restTemplate.get(env, "apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/lock",
NamespaceLockDTO.class,
appId, clusterName, namespaceName);
}
}
......
package com.ctrip.framework.apollo.portal.service;
package com.ctrip.framework.apollo.portal.api;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.PortalSettings;
import com.dianping.cat.Cat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.exception.ServiceException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author liuym
*/
@Service
public class ServiceLocator {
import javax.annotation.PostConstruct;
private static final Logger logger = LoggerFactory.getLogger(ServiceLocator.class);
@Component
public class AdminServiceAddressLocator {
private static final int DEFAULT_TIMEOUT_MS = 1000;
private static final long REFRESH_INTERVAL = 5 * 60 * 1000;
private static final int RETRY_TIMES = 3;
private static final int CALL_META_SERVER_THRESHOLD = 2;
private static final String ADMIN_SERVICE_URL_PATH = "/services/admin";
private ScheduledExecutorService refreshServiceAddressService;
private RestTemplate restTemplate;
private List<Env> allEnvs;
private Map<Env, List<ServiceDTO>> cache = new ConcurrentHashMap<>();
@Autowired
private HttpMessageConverters httpMessageConverters;
private Map<Env, ServiceDTO[]> serviceAddressCache = new ConcurrentHashMap<>();
private final AtomicInteger adminCallCounts = new AtomicInteger(0);
@Autowired
private PortalSettings portalSettings;
@PostConstruct
private void postConstruct() {
public void init() {
allEnvs = portalSettings.getAllEnvs();
//init restTemplate
restTemplate = new RestTemplate(httpMessageConverters.getConverters());
if (restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) {
SimpleClientHttpRequestFactory rf =
......@@ -60,58 +58,52 @@ public class ServiceLocator {
rf.setReadTimeout(DEFAULT_TIMEOUT_MS);
rf.setConnectTimeout(DEFAULT_TIMEOUT_MS);
}
refreshServiceAddressService = Executors.newScheduledThreadPool(1);
refreshServiceAddressService.scheduleWithFixedDelay(
new RefreshAdminServerAddressTask(), 0, REFRESH_INTERVAL,
TimeUnit.MILLISECONDS);
}
public List<ServiceDTO> getServiceList(Env env) {
return cache.get(env);
}
public ServiceDTO getServiceAddress(Env env) throws ServiceException {
//Maintain admin server address
class RefreshAdminServerAddressTask implements Runnable {
if (adminCallCounts.get() % CALL_META_SERVER_THRESHOLD == 0) {
return getServiceAddressFromMetaServer(env);
} else {
//if cached then return from cache
ServiceDTO[] serviceDTOs = serviceAddressCache.get(env);
if (serviceDTOs != null && serviceDTOs.length > 0){
return randomServiceAddress(serviceDTOs);
}else {//return from meta server
return getServiceAddressFromMetaServer(env);
@Override
public void run() {
for (Env env : allEnvs) {
refreshServerAddressCache(env);
}
}
}
public ServiceDTO getServiceAddressFromMetaServer(Env env) {
//retry
private void refreshServerAddressCache(Env env) {
for (int i = 0; i < RETRY_TIMES; i++) {
ServiceDTO[] services = getServices(env);
if (services != null && services.length > 0) {
serviceAddressCache.put(env, services);
return randomServiceAddress(services);
} else {
logger.warn(String.format("can not get %s admin service address at %d time", env, i));
try {
ServiceDTO[] services = getAdminServerAddress(env);
if (services == null || services.length == 0) {
continue;
}
cache.put(env, Arrays.asList(services));
break;
} catch (Throwable e) {//meta server error
Cat.logError("get admin server address fail", e);
continue;
}
}
//clear cache
serviceAddressCache.remove(env);
logger.error(String.format("can not get %s admin service address", env));
throw new ServiceException("No available admin service");
}
private ServiceDTO[] getServices(Env env) {
private ServiceDTO[] getAdminServerAddress(Env env) {
String domainName = MetaDomainConsts.getDomain(env);
String url = domainName + ADMIN_SERVICE_URL_PATH;
try {
return restTemplate.getForObject(new URI(url), ServiceDTO[].class);
} catch (Exception ex) {
logger.warn(ex.getMessage());
return null;
}
return restTemplate.getForObject(url, ServiceDTO[].class);
}
private ServiceDTO randomServiceAddress(ServiceDTO[] services){
return services[Math.abs(adminCallCounts.getAndIncrement()) % services.length];
}
}
package com.ctrip.framework.apollo.portal.api;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.exception.ServiceException;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriTemplateHandler;
import java.net.SocketTimeoutException;
import java.util.List;
import javax.annotation.PostConstruct;
/**
* 封装RestTemplate. admin server集群在某些机器宕机或者超时的情况下轮询重试
*/
@Component
public class RetryableRestTemplate {
private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler();
private RestTemplate restTemplate;
@Autowired
private RestTemplateFactory restTemplateFactory;
@Autowired
private AdminServiceAddressLocator adminServiceAddressLocator;
@PostConstruct
private void postConstruct() {
restTemplate = restTemplateFactory.getObject();
}
public <T> T get(Env env, String path, Class<T> responseType, Object... urlVariables)
throws RestClientException {
return execute(HttpMethod.GET, env, path, null, responseType, urlVariables);
}
public <T> T post(Env env, String path, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
return execute(HttpMethod.POST, env, path, request, responseType, uriVariables);
}
public void put(Env env, String path, Object request, Object... urlVariables) throws RestClientException {
execute(HttpMethod.PUT, env, path, request, null, urlVariables);
}
public void delete(Env env, String path, Object... urlVariables) throws RestClientException {
execute(HttpMethod.DELETE, env, path, null, null, urlVariables);
}
private <T> T execute(HttpMethod method, Env env, String path, Object request, Class<T> responseType,
Object... uriVariables) {
String uri = uriTemplateHandler.expand(path, uriVariables).getPath();
Transaction ct = Cat.newTransaction("AdminAPI", uri);
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;
}
for (ServiceDTO serviceDTO : services) {
try {
T result = doExecute(method, serviceDTO, path, request, responseType, uriVariables);
ct.setStatus(Message.SUCCESS);
ct.complete();
return result;
} catch (Throwable t) {
Cat.logError(t);
if (canRetry(t, method)) {
Cat.logEvent(CatEventType.API_RETRY, uri);
continue;
} else {//biz exception rethrow
ct.setStatus(t);
ct.complete();
throw t;
}
}
}
//all admin server down
ServiceException e = new ServiceException("No available admin service");
ct.setStatus(e);
ct.complete();
throw e;
}
private <T> T doExecute(HttpMethod method, ServiceDTO service, String path, Object request,
Class<T> responseType,
Object... uriVariables) {
T result = null;
switch (method) {
case GET:
result = restTemplate.getForObject(parseHost(service) + path, responseType, uriVariables);
break;
case POST:
result =
restTemplate.postForEntity(parseHost(service) + path, request, responseType, uriVariables).getBody();
break;
case PUT:
restTemplate.put(parseHost(service) + path, request, uriVariables);
break;
case DELETE:
restTemplate.delete(parseHost(service) + path, uriVariables);
break;
default:
throw new UnsupportedOperationException(String.format("not supported http method(method=%s)", method));
}
return result;
}
private String parseHost(ServiceDTO serviceAddress) {
return serviceAddress.getHomepageUrl() + "/";
}
//post,delete,put请求在admin server处理超时情况下不重试
private boolean canRetry(Throwable e, HttpMethod method) {
Throwable nestedException = e.getCause();
if (method == HttpMethod.GET) {
return nestedException instanceof SocketTimeoutException
|| nestedException instanceof HttpHostConnectException
|| nestedException instanceof ConnectTimeoutException;
} else {
return nestedException instanceof HttpHostConnectException
|| nestedException instanceof ConnectTimeoutException;
}
}
}
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.ctrip;
import com.ctrip.framework.apollo.portal.auth.LogoutHandler;
import com.ctrip.framework.apollo.portal.service.ServerConfigService;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -11,7 +12,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class CtripLogoutHandler implements LogoutHandler{
public class CtripLogoutHandler implements LogoutHandler {
@Autowired
private ServerConfigService serverConfigService;
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.ctrip;
import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler;
import java.io.IOException;
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.ctrip;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.entity.po.UserInfo;
import java.lang.reflect.Method;
......@@ -7,7 +8,7 @@ import java.lang.reflect.Method;
/**
* ctrip内部实现的获取用户信息
*/
public class CtripUserInfoHolder implements UserInfoHolder{
public class CtripUserInfoHolder implements UserInfoHolder {
private Object assertionHolder;
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.ctrip;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
......@@ -62,7 +62,7 @@ public class CtripUserService implements UserService {
}
@Override
public List<UserInfo> searchUsers(String keyword, int offset, int limit) {
public List<UserInfo> searchUsers(String keyword, int offset, int limit) {
UserServiceRequest request = assembleSearchUserRequest(keyword, offset, limit);
HttpEntity<UserServiceRequest> entity = new HttpEntity<>(request);
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.defaultimpl;
import com.ctrip.framework.apollo.portal.auth.LogoutHandler;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DefaultLogoutHandler implements LogoutHandler{
public class DefaultLogoutHandler implements LogoutHandler {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response) {
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.defaultimpl;
import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler;
import java.io.IOException;
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.defaultimpl;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.entity.po.UserInfo;
/**
* 不是ctrip的公司默认提供一个假用户
*/
public class DefaultUserInfoHolder implements UserInfoHolder{
public class DefaultUserInfoHolder implements UserInfoHolder {
public DefaultUserInfoHolder(){
......
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.defaultimpl;
import com.google.common.collect.Lists;
......
package com.ctrip.framework.apollo.portal.configutation;
import com.ctrip.framework.apollo.portal.auth.CtripLogoutHandler;
import com.ctrip.framework.apollo.portal.auth.CtripSsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.auth.CtripUserInfoHolder;
import com.ctrip.framework.apollo.portal.auth.CtripUserService;
import com.ctrip.framework.apollo.portal.auth.DefaultLogoutHandler;
import com.ctrip.framework.apollo.portal.auth.DefaultSsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.auth.DefaultUserInfoHolder;
import com.ctrip.framework.apollo.portal.auth.DefaultUserService;
import com.ctrip.framework.apollo.portal.auth.ctrip.CtripLogoutHandler;
import com.ctrip.framework.apollo.portal.auth.ctrip.CtripSsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserInfoHolder;
import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserService;
import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultLogoutHandler;
import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultSsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultUserInfoHolder;
import com.ctrip.framework.apollo.portal.auth.defaultimpl.DefaultUserService;
import com.ctrip.framework.apollo.portal.auth.LogoutHandler;
import com.ctrip.framework.apollo.portal.auth.SsoHeartbeatHandler;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
......
package com.ctrip.framework.apollo.portal.constant;
public interface CatEventType {
String RELEASE_NAMESPACE = "Namespace.Release";
String MODIFY_NAMESPACE_BY_TEXT = "Namespace.Modify.Text";
String MODIFY_NAMESPACE = "Namespace.Modify";
String SYNC_NAMESPACE = "Namespace.Sync";
String CREATE_APP = "App.Create";
String CREATE_CLUSTER = "Cluster.Create";
String CREATE_NAMESPACE = "Namespace.Create";
String API_RETRY = "API.Retry";
}
......@@ -10,12 +10,12 @@ public class NamespaceReleaseModel implements Verifiable {
private String env;
private String clusterName;
private String namespaceName;
private String releaseBy;
private String releaseTitle;
private String releaseComment;
@Override
public boolean isInvalid() {
return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, releaseBy);
return StringUtils.isContainEmpty(appId, env, clusterName, namespaceName, releaseTitle);
}
public String getAppId() {
......@@ -50,12 +50,12 @@ public class NamespaceReleaseModel implements Verifiable {
this.namespaceName = namespaceName;
}
public String getReleaseBy() {
return releaseBy;
public String getReleaseTitle() {
return releaseTitle;
}
public void setReleaseBy(String releaseBy) {
this.releaseBy = releaseBy;
public void setReleaseTitle(String releaseTitle) {
this.releaseTitle = releaseTitle;
}
public String getReleaseComment() {
......
......@@ -6,7 +6,7 @@ import com.ctrip.framework.apollo.core.dto.AppNamespaceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.PortalSettings;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.service.RoleInitializationService;
import com.dianping.cat.Cat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -27,8 +27,6 @@ public class CreationListener {
private AdminServiceAPI.AppAPI appAPI;
@Autowired
private AdminServiceAPI.NamespaceAPI namespaceAPI;
@Autowired
private RoleInitializationService roleInitializationService;
@EventListener
public void onAppCreationEvent(AppCreationEvent event) {
......@@ -38,20 +36,22 @@ public class CreationListener {
try {
appAPI.createApp(env, appDTO);
} catch (Throwable e) {
logger.error("call appAPI.createApp error.[{app}, {env}]", appDTO.getAppId(), env, e);
logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appDTO.getAppId(), env, e);
Cat.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appDTO.getAppId(), env), e);
}
}
}
@EventListener
public void onAppNamespaceCreationEvent(AppNamespaceCreationEvent event){
AppNamespaceDTO dto = BeanUtils.transfrom(AppNamespaceDTO.class, event.getAppNamespace());
AppNamespaceDTO appNamespace = BeanUtils.transfrom(AppNamespaceDTO.class, event.getAppNamespace());
List<Env> envs = portalSettings.getActiveEnvs();
for (Env env : envs) {
try {
namespaceAPI.createAppNamespace(env, dto);
namespaceAPI.createAppNamespace(env, appNamespace);
} catch (Throwable e) {
logger.error("call namespaceAPI.createAppNamespace error. [{app}, {env}]", dto.getAppId(), env, e);
logger.error("call appAPI.createApp error.(appId={appId}, env={env})", appNamespace.getAppId(), env, e);
Cat.logError(String.format("call appAPI.createApp error. (appId=%s, env=%s)", appNamespace.getAppId(), env), e);
}
}
}
......
......@@ -20,8 +20,10 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.vo.EnvClusterInfo;
import com.ctrip.framework.apollo.portal.repository.AppRepository;
import com.dianping.cat.Cat;
@Service
public class AppService {
......@@ -94,6 +96,8 @@ public class AppService {
appNamespaceService.createDefaultAppNamespace(appId);
//role
roleInitializationService.initAppRoles(createdApp);
Cat.logEvent(CatEventType.CREATE_APP, appId);
return createdApp;
}
}
......
......@@ -4,6 +4,8 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.dto.ClusterDTO;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.dianping.cat.Cat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -24,7 +26,11 @@ public class ClusterService {
if (!clusterAPI.isClusterUnique(cluster.getAppId(), env, cluster.getName())){
throw new BadRequestException(String.format("cluster %s already exists.", cluster.getName()));
}
return clusterAPI.create(env, cluster);
ClusterDTO clusterDTO = clusterAPI.create(env, cluster);
Cat.logEvent(CatEventType.CREATE_CLUSTER, cluster.getAppId(), "0", cluster.getName());
return clusterDTO;
}
}
package com.ctrip.framework.apollo.portal.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
......@@ -11,20 +9,23 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.dto.ItemChangeSets;
import com.ctrip.framework.apollo.core.dto.ItemDTO;
import com.ctrip.framework.apollo.core.dto.NamespaceDTO;
import com.ctrip.framework.apollo.core.exception.BadRequestException;
import com.ctrip.framework.apollo.core.exception.ServiceException;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ItemDiffs;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceIdentifer;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceTextModel;
import com.ctrip.framework.apollo.portal.service.txtresolver.ConfigTextResolver;
import com.dianping.cat.Cat;
import java.util.LinkedList;
import java.util.List;
......@@ -33,8 +34,6 @@ import java.util.Map;
@Service
public class ConfigService {
private Logger logger = LoggerFactory.getLogger(ConfigService.class);
@Autowired
private UserInfoHolder userInfoHolder;
@Autowired
......@@ -77,6 +76,9 @@ public class ConfigService {
changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId());
itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets);
Cat.logEvent(CatEventType.MODIFY_NAMESPACE_BY_TEXT, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
Cat.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
}
......@@ -92,7 +94,9 @@ public class ConfigService {
item.setDataChangeCreatedBy(username);
item.setDataChangeLastModifiedBy(username);
return itemAPI.createItem(appId, env, clusterName, namespaceName, item);
ItemDTO itemDTO = itemAPI.createItem(appId, env, clusterName, namespaceName, item);
Cat.logEvent(CatEventType.MODIFY_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
return itemDTO;
}
public void updateItem(String appId, Env env, String clusterName, String namespaceName, ItemDTO item) {
......@@ -115,17 +119,16 @@ public class ConfigService {
NamespaceIdentifer namespaceIdentifer = itemDiff.getNamespace();
ItemChangeSets changeSets = itemDiff.getDiffs();
changeSets.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId());
try {
itemAPI
.updateItemsByChangeSet(namespaceIdentifer.getAppId(), namespaceIdentifer.getEnv(),
namespaceIdentifer.getClusterName(),
namespaceIdentifer.getNamespaceName(), changeSets);
} catch (HttpClientErrorException e) {
logger.error("sync items error. namespace:{}", namespaceIdentifer);
throw new ServiceException(String.format("sync item error. env:%s, clusterName:%s", namespaceIdentifer.getEnv(),
namespaceIdentifer.getClusterName()), e);
String appId = namespaceIdentifer.getAppId();
Env env = namespaceIdentifer.getEnv();
String clusterName = namespaceIdentifer.getClusterName();
String namespaceName = namespaceIdentifer.getNamespaceName();
itemAPI.updateItemsByChangeSet(appId, env, clusterName, namespaceName, changeSets);
Cat.logEvent(CatEventType.SYNC_NAMESPACE, String.format("%s+%s+%s+%s", appId, env, clusterName, namespaceName));
}
}
}
public List<ItemDiffs> compare(List<NamespaceIdentifer> comparedNamespaces, List<ItemDTO> sourceItems) {
......
......@@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
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.NamespaceDTO;
import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
......@@ -13,14 +12,14 @@ import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.vo.NamespaceVO;
import com.dianping.cat.Cat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import java.util.Collections;
import java.util.HashMap;
......@@ -42,13 +41,10 @@ public class NamespaceService {
private AdminServiceAPI.ReleaseAPI releaseAPI;
@Autowired
private AdminServiceAPI.NamespaceAPI namespaceAPI;
@Autowired
private AppNamespaceService appNamespaceService;
public NamespaceDTO createNamespace(Env env, NamespaceDTO namespace) {
if (StringUtils.isEmpty(namespace.getDataChangeCreatedBy())) {
namespace.setDataChangeCreatedBy(userInfoHolder.getUser().getUserId());
......@@ -56,6 +52,9 @@ public class NamespaceService {
namespace.setDataChangeLastModifiedBy(userInfoHolder.getUser().getUserId());
NamespaceDTO createdNamespace = namespaceAPI.createNamespace(env, namespace);
Cat.logEvent(CatEventType.CREATE_NAMESPACE,
String.format("%s+%s+%s+%s", namespace.getAppId(), env, namespace.getClusterName(),
namespace.getNamespaceName()));
return createdNamespace;
}
......@@ -100,17 +99,11 @@ public class NamespaceService {
String namespaceName = namespace.getNamespaceName();
//latest Release
ReleaseDTO release = null;
ReleaseDTO latestRelease = null;
Map<String, String> releaseItems = new HashMap<>();
try {
release = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
releaseItems = gson.fromJson(release.getConfigurations(), Map.class);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
logger.warn(ExceptionUtils.toString(e));
} else {
throw e;
}
latestRelease = releaseAPI.loadLatestRelease(appId, env, clusterName, namespaceName);
if (latestRelease != null){
releaseItems = gson.fromJson(latestRelease.getConfigurations(), Map.class);
}
//not Release config items
......@@ -161,6 +154,7 @@ public class NamespaceService {
namespace.setPublic(isPublic);
}
private List<NamespaceVO.ItemVO> parseDeletedItems(List<ItemDTO> newItems, Map<String, String> releaseItems) {
Map<String, ItemDTO> newItemMap = BeanUtils.mapByKey("key", newItems);
......
......@@ -6,8 +6,10 @@ import com.ctrip.framework.apollo.core.dto.ReleaseDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
import com.ctrip.framework.apollo.portal.auth.UserInfoHolder;
import com.ctrip.framework.apollo.portal.constant.CatEventType;
import com.ctrip.framework.apollo.portal.entity.form.NamespaceReleaseModel;
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseVO;
import com.dianping.cat.Cat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -22,6 +24,7 @@ import java.util.Set;
@Service
public class ReleaseService {
private static final Gson gson = new Gson();
@Autowired
......@@ -30,26 +33,33 @@ public class ReleaseService {
private AdminServiceAPI.ReleaseAPI releaseAPI;
public ReleaseDTO createRelease(NamespaceReleaseModel model) {
return releaseAPI.release(model.getAppId(), model.getEnv(), model.getClusterName(),
model.getNamespaceName(), model.getReleaseBy(), model.getReleaseComment()
, userInfoHolder.getUser().getUserId());
String appId = model.getAppId();
Env env = model.getEnv();
String clusterName = model.getClusterName();
String namespaceName = model.getNamespaceName();
ReleaseDTO releaseDTO =
releaseAPI.release(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));
return releaseDTO;
}
public List<ReleaseVO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page, int size){
public List<ReleaseVO> findReleases(String appId, Env env, String clusterName, String namespaceName, int page,
int size) {
List<ReleaseDTO> releaseDTOs = releaseAPI.findReleases(appId, env, clusterName, namespaceName, page, size);
if (CollectionUtils.isEmpty(releaseDTOs)){
if (CollectionUtils.isEmpty(releaseDTOs)) {
return Collections.EMPTY_LIST;
}
List<ReleaseVO> releases = new LinkedList<>();
for (ReleaseDTO releaseDTO: releaseDTOs){
for (ReleaseDTO releaseDTO : releaseDTOs) {
ReleaseVO release = new ReleaseVO();
release.setBaseInfo(releaseDTO);
Set<ReleaseVO.KVEntity> kvEntities = new LinkedHashSet<>();
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()));
}
release.setItems(kvEntities);
......
......@@ -57,7 +57,7 @@
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提交</button>
<button type="submit" class="btn btn-primary" ng-disabled="submitBtnDisabled">提交</button>
</div>
</div>
</form>
......
......@@ -40,7 +40,7 @@
<div class="form-group">
<apollouserselector apollo-id="userSelectWidgetId"></apollouserselector>
</div>
<button type="submit" class="btn btn-default" style="margin-left: 20px;">添加</button>
<button type="submit" class="btn btn-default" style="margin-left: 20px;" ng-disabled="submitBtnDisabled">添加</button>
</form>
<!-- Split button -->
<div class="user-container">
......
......@@ -70,7 +70,7 @@
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">提交</button>
<button type="submit" class="btn btn-primary" ng-disabled="submitBtnDisabled">提交</button>
</div>
</div>
</form>
......
......@@ -225,7 +225,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">发布
<button type="submit" class="btn btn-primary" ng-disabled="releaseBtnDisabled">发布
</button>
</div>
</div>
......@@ -308,7 +308,7 @@
<button type="button" class="btn btn-default" data-dismiss="modal">关闭
</button>
<button type="submit" class="btn btn-primary"
ng-show="tableViewOperType != 'retrieve'">提交
ng-show="tableViewOperType != 'retrieve'" ng-disabled="addItemBtnDisabled && tableViewOperType == 'create'">提交
</button>
</div>
</div>
......
......@@ -32,7 +32,7 @@
ng-click="diff()">下一步
</button>
<button type="button" class="btn btn-success" ng-show="syncItemStep == 2 && hasDiff"
ng-click="syncItems()">同步
ng-click="syncItems()" ng-disabled="syncBtnDisabled">同步
</button>
<button type="button" class="btn btn-info" data-dismiss="modal"
ng-click="backToAppHomePage()">返回到项目首页
......
......@@ -114,7 +114,7 @@
<div class="form-group">
<div class="col-sm-offset-3 col-sm-10">
<button type="submit" class="btn btn-primary">提交</button>
<button type="submit" class="btn btn-primary" ng-disabled="submitBtnDisabled">提交</button>
</div>
</div>
</form>
......
......@@ -40,7 +40,7 @@
<div class="form-group">
<apollouserselector apollo-id="modifyRoleWidgetId"></apollouserselector>
</div>
<button type="submit" class="btn btn-default" style="margin-left: 20px;">添加</button>
<button type="submit" class="btn btn-default" style="margin-left: 20px;" ng-disabled="modifyRoleSubmitBtnDisabled">添加</button>
</form>
<!-- Split button -->
<div class="user-container">
......@@ -70,7 +70,7 @@
<apollouserselector apollo-id="releaseRoleWidgetId"></apollouserselector>
</div>
<button type="submit" class="btn btn-default" style="margin-left: 20px;">添加</button>
<button type="submit" class="btn btn-default" style="margin-left: 20px;" ng-disabled="ReleaseRoleSubmitBtnDisabled">添加</button>
</form>
<!-- Split button -->
<div class="user-container">
......
create_app_module.controller('CreateAppController', ['$scope', '$window', 'toastr', 'AppService', 'UserService', 'AppUtil', 'OrganizationService',
function ($scope, $window, toastr, AppService, UserService, AppUtil, OrganizationService) {
$scope.submitBtnDisabled = false;
OrganizationService.find_organizations().then(function (result) {
var organizations = [];
result.forEach(function (item) {
......@@ -47,12 +49,15 @@ create_app_module.controller('CreateAppController', ['$scope', '$window', 'toast
}
$scope.app.ownerName = user.id;
$scope.submitBtnDisabled = true;
AppService.create($scope.app).then(function (result) {
toastr.success('添加成功!');
setInterval(function () {
$scope.submitBtnDisabled = false;
$window.location.href = '/config.html?#appid=' + result.appId;
}, 1000);
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), '添加失败!');
});
};
......
......@@ -9,6 +9,8 @@ cluster_module.controller('ClusterController',
$scope.step = 1;
$scope.submitBtnDisabled = false;
EnvService.find_all_envs().then(function (result) {
$scope.envs = [];
result.forEach(function (env) {
......@@ -35,6 +37,7 @@ cluster_module.controller('ClusterController',
$scope.envs.forEach(function (env) {
if (env.checked) {
noEnvChecked = false;
$scope.submitBtnDisabled = true;
ClusterService.create_cluster($scope.appId, env.name,
{
name: $scope.clusterName,
......@@ -42,8 +45,10 @@ cluster_module.controller('ClusterController',
}).then(function (result) {
toastr.success(env.name, "集群创建成功");
$scope.step = 2;
$scope.submitBtnDisabled = false;
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "集群创建失败");
$scope.submitBtnDisabled = false;
})
}
});
......
......@@ -10,6 +10,8 @@ namespace_module.controller("LinkNamespaceController",
$scope.step = 1;
$scope.submitBtnDisabled = false;
PermissionService.has_root_permission().then(function (result) {
$scope.hasRootPermission = result.hasPermission;
});
......@@ -90,23 +92,29 @@ namespace_module.controller("LinkNamespaceController",
}
});
});
$scope.submitBtnDisabled = true;
NamespaceService.createNamespace($scope.appId, namespaceCreationModels)
.then(function (result) {
toastr.success("创建成功");
$scope.step = 2;
setInterval(function () {
$scope.submitBtnDisabled = false;
$window.location.href =
'/namespace/role.html?#appid=' + $scope.appId
+ "&namespaceName=" + $scope.namespaceName;
}, 1000);
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result));
});
} else {
$scope.submitBtnDisabled = true;
NamespaceService.createAppNamespace($scope.appId, $scope.appNamespace).then(
function (result) {
$scope.step = 2;
setInterval(function () {
$scope.submitBtnDisabled = false;
if ($scope.appNamespace.isPublic) {
$window.location.reload();
} else {//private的直接link并且跳转到授权页面
......@@ -116,6 +124,7 @@ namespace_module.controller("LinkNamespaceController",
}
}, 1000);
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "创建失败");
});
}
......
......@@ -38,7 +38,12 @@ application_module.controller("ConfigNamespaceController",
$scope.createItem = createItem;
$scope.doItem = doItem;
$scope.releaseBtnDisabled = false;
$scope.addItemBtnDisabled = false;
$scope.commitChangeBtnDisabled = false;
PermissionService.get_app_role_users($rootScope.pageContext.appId)
.then(function (result) {
......@@ -91,6 +96,12 @@ application_module.controller("ConfigNamespaceController",
namespaceId: namespace.namespace.id,
format: namespace.format
};
//prevent repeat submit
if ($scope.commitChangeBtnDisabled){
return;
}
$scope.commitChangeBtnDisabled = true;
ConfigService.modify_items($rootScope.pageContext.appId, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
namespace.namespace.namespaceName,
......@@ -99,14 +110,17 @@ application_module.controller("ConfigNamespaceController",
toastr.success("更新成功, 如需生效请发布");
//refresh all namespace items
$rootScope.refreshNamespaces();
$scope.commitChangeBtnDisabled = false;
return true;
}, function (result) {
toastr.error(AppUtil.errorMsg(result), "更新失败");
$scope.commitChangeBtnDisabled = false;
return false;
}
);
}
var releaseModal = $('#releaseModal');
$scope.toReleaseNamespace = {};
function prepareReleaseNamespace(namespace) {
......@@ -125,6 +139,7 @@ application_module.controller("ConfigNamespaceController",
$scope.releaseComment = '';
function release() {
$scope.releaseBtnDisabled = true;
ReleaseService.release($rootScope.pageContext.appId, $rootScope.pageContext.env,
$rootScope.pageContext.clusterName,
$scope.toReleaseNamespace.namespace.namespaceName,
......@@ -134,9 +149,11 @@ application_module.controller("ConfigNamespaceController",
releaseModal.modal('hide');
toastr.success("发布成功");
//refresh all namespace items
$scope.releaseBtnDisabled = false;
$rootScope.refreshNamespaces();
}, function (result) {
$scope.releaseBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "发布失败");
}
......@@ -240,6 +257,7 @@ application_module.controller("ConfigNamespaceController",
return;
}
$scope.addItemBtnDisabled = true;
ConfigService.create_item($rootScope.pageContext.appId,
cluster.env,
cluster.name,
......@@ -249,8 +267,10 @@ application_module.controller("ConfigNamespaceController",
toastr.success(cluster.env + " , " + $scope.item.key,
"添加成功");
itemModal.modal('hide');
$scope.addItemBtnDisabled = false;
$rootScope.refreshNamespaces(namespace_view_type.TABLE);
}, function (result) {
$scope.addItemBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
......
......@@ -11,6 +11,7 @@ sync_item_module.controller("SyncItemController",
};
$scope.syncBtnDisabled = false;
////// load items //////
ConfigService.find_items($scope.pageContext.appId, $scope.pageContext.env,
......@@ -51,15 +52,20 @@ sync_item_module.controller("SyncItemController",
toastr.error(AppUtil.errorMsg(result));
});
};
$scope.syncItems = function () {
ConfigService.sync_items($scope.pageContext.appId, $scope.pageContext.namespaceName, parseSyncSourceData()).then(function (result) {
$scope.syncItemStep += 1;
$scope.syncSuccess = true;
}, function (result) {
$scope.syncSuccess = false;
toastr.error(AppUtil.errorMsg(result));
});
$scope.syncBtnDisabled = true;
ConfigService.sync_items($scope.pageContext.appId,
$scope.pageContext.namespaceName,
parseSyncSourceData()).then(function (result) {
$scope.syncItemStep += 1;
$scope.syncSuccess = true;
$scope.syncBtnDisabled = false;
}, function (result) {
$scope.syncSuccess = false;
$scope.syncBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result));
});
};
var selectedClusters = [];
......
......@@ -7,6 +7,8 @@ role_module.controller('AppRoleController',
appId: params.appid
};
$scope.submitBtnDisabled = false;
$scope.userSelectWidgetId = 'toAssignMasterRoleUser';
PermissionService.has_assign_user_permission($scope.pageContext.appId)
......@@ -31,13 +33,16 @@ role_module.controller('AppRoleController',
return;
}
var toAssignMasterRoleUser = user.id;
$scope.submitBtnDisabled = true;
PermissionService.assign_master_role($scope.pageContext.appId,
toAssignMasterRoleUser)
.then(function (result) {
$scope.submitBtnDisabled = false;
toastr.success("添加成功");
$scope.appRoleUsers.masterUsers.push({userId: toAssignMasterRoleUser});
$('.' + $scope.userSelectWidgetId).select2("val", "");
}, function (result) {
$scope.submitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
};
......
......@@ -10,6 +10,9 @@ role_module.controller('NamespaceRoleController',
namespaceName: params.namespaceName
};
$scope.modifyRoleSubmitBtnDisabled = false;
$scope.ReleaseRoleSubmitBtnDisabled = false;
$scope.releaseRoleWidgetId = 'releaseRoleWidgetId';
$scope.modifyRoleWidgetId = 'modifyRoleWidgetId';
......@@ -35,16 +38,19 @@ role_module.controller('NamespaceRoleController',
toastr.warning("请选择用户");
return;
}
$scope.ReleaseRoleSubmitBtnDisabled = true;
var toAssignReleaseNamespaceRoleUser = user.id;
PermissionService.assign_release_namespace_role($scope.pageContext.appId,
$scope.pageContext.namespaceName,
toAssignReleaseNamespaceRoleUser)
.then(function (result) {
toastr.success("添加成功");
$scope.ReleaseRoleSubmitBtnDisabled = false;
$scope.rolesAssignedUsers.releaseRoleUsers.push(
{userId: toAssignReleaseNamespaceRoleUser});
$('.' + $scope.releaseRoleWidgetId).select2("val", "");
}, function (result) {
$scope.ReleaseRoleSubmitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
} else {
......@@ -53,16 +59,19 @@ role_module.controller('NamespaceRoleController',
toastr.warning("请选择用户");
return;
}
$scope.modifyRoleSubmitBtnDisabled = true;
var toAssignModifyNamespaceRoleUser = user.id;
PermissionService.assign_modify_namespace_role($scope.pageContext.appId,
$scope.pageContext.namespaceName,
toAssignModifyNamespaceRoleUser)
.then(function (result) {
toastr.success("添加成功");
$scope.modifyRoleSubmitBtnDisabled = false;
$scope.rolesAssignedUsers.modifyRoleUsers.push(
{userId: toAssignModifyNamespaceRoleUser});
$('.' + $scope.modifyRoleWidgetId).select2("val", "");
}, function (result) {
$scope.modifyRoleSubmitBtnDisabled = false;
toastr.error(AppUtil.errorMsg(result), "添加失败");
});
}
......
......@@ -17,7 +17,8 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({
ConfigServiceTest.class, PropertyResolverTest.class,
NamespaceServiceTest.class, ServiceExceptionTest.class, RolePermissionServiceTest.class,
AppNamespaceServiceTest.class, RoleInitializationServiceTest.class, FileTextResolverTest.class
AppNamespaceServiceTest.class, RoleInitializationServiceTest.class, FileTextResolverTest.class,
RetryableRestTemplateTest.class
})
public class AllTests {
......
package com.ctrip.framework.apollo.portal;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.exception.ServiceException;
import com.ctrip.framework.apollo.portal.api.AdminServiceAddressLocator;
import com.ctrip.framework.apollo.portal.api.RetryableRestTemplate;
import org.apache.http.HttpHost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Collections;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class RetryableRestTemplateTest extends AbstractUnitTest {
@Mock
private AdminServiceAddressLocator serviceAddressLocator;
@Mock
private RestTemplate restTemplate;
@InjectMocks
private RetryableRestTemplate retryableRestTemplate;
private String path = "app";
private String serviceOne = "http://10.0.0.1";
private String serviceTwo = "http://10.0.0.2";
private String serviceThree = "http://10.0.0.3";
private ResourceAccessException socketTimeoutException = new ResourceAccessException("");
private ResourceAccessException httpHostConnectException = new ResourceAccessException("");
private ResourceAccessException connectTimeoutException = new ResourceAccessException("");
private Object request = new Object();
private ResponseEntity<Object> entity = new ResponseEntity<>(HttpStatus.OK);
@Before
public void init() {
socketTimeoutException.initCause(new SocketTimeoutException());
httpHostConnectException
.initCause(new HttpHostConnectException(new HttpHost(serviceOne, 80), new ConnectException()));
connectTimeoutException.initCause(new ConnectTimeoutException());
}
@Test(expected = ServiceException.class)
public void testNoAdminServer() {
when(serviceAddressLocator.getServiceList(any())).thenReturn(Collections.emptyList());
retryableRestTemplate.get(Env.DEV, path, Object.class);
}
@Test(expected = ServiceException.class)
public void testAllServerDown() {
when(serviceAddressLocator.getServiceList(any()))
.thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree)));
when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException);
when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenThrow(httpHostConnectException);
when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException);
retryableRestTemplate.get(Env.DEV, path, Object.class);
verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class);
verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class);
verify(restTemplate).getForObject(serviceThree + "/" + path, Object.class);
}
@Test
public void testOneServerDown() {
Object result = new Object();
when(serviceAddressLocator.getServiceList(any()))
.thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree)));
when(restTemplate.getForObject(serviceOne + "/" + path, Object.class)).thenThrow(socketTimeoutException);
when(restTemplate.getForObject(serviceTwo + "/" + path, Object.class)).thenReturn(result);
when(restTemplate.getForObject(serviceThree + "/" + path, Object.class)).thenThrow(connectTimeoutException);
Object o = retryableRestTemplate.get(Env.DEV, path, Object.class);
verify(restTemplate).getForObject(serviceOne + "/" + path, Object.class);
verify(restTemplate).getForObject(serviceTwo + "/" + path, Object.class);
verify(restTemplate, times(0)).getForObject(serviceThree + "/" + path, Object.class);
Assert.assertEquals(result, o);
}
@Test(expected = ResourceAccessException.class)
public void testPostSocketTimeoutNotRetry(){
when(serviceAddressLocator.getServiceList(any()))
.thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree)));
when(restTemplate.postForEntity(serviceOne + "/" + path, request, Object.class)).thenThrow(socketTimeoutException);
when(restTemplate.postForEntity(serviceTwo + "/" + path, request, Object.class)).thenReturn(entity);
retryableRestTemplate.post(Env.DEV, path, request, Object.class);
verify(restTemplate).postForEntity(serviceOne + "/" + path, request, Object.class);
verify(restTemplate, times(0)).postForEntity(serviceTwo + "/" + path, request, Object.class);
}
@Test
public void testDelete(){
when(serviceAddressLocator.getServiceList(any()))
.thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree)));
retryableRestTemplate.delete(Env.DEV, path);
verify(restTemplate).delete(serviceOne + "/" + path);
}
@Test
public void testPut(){
when(serviceAddressLocator.getServiceList(any()))
.thenReturn(Arrays.asList(mockService(serviceOne), mockService(serviceTwo), mockService(serviceThree)));
retryableRestTemplate.put(Env.DEV, path, request);
verify(restTemplate).put(serviceOne + "/" + path, request);
}
private ServiceDTO mockService(String homeUrl) {
ServiceDTO serviceDTO = new ServiceDTO();
serviceDTO.setHomepageUrl(homeUrl);
return serviceDTO;
}
}
package com.ctrip.framework.apollo.portal.auth;
package com.ctrip.framework.apollo.portal.auth.ctrip;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.ctrip.framework.apollo.portal.AbstractUnitTest;
import com.ctrip.framework.apollo.portal.auth.ctrip.CtripUserService;
import com.ctrip.framework.apollo.portal.entity.po.UserInfo;
import com.ctrip.framework.apollo.portal.service.ServerConfigService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
......@@ -32,8 +32,7 @@ import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith(MockitoJUnitRunner.class)
public class CtripUserServiceTest {
public class CtripUserServiceTest extends AbstractUnitTest{
private CtripUserService ctripUserService;
private String someUserServiceUrl;
private String someUserServiceToken;
......@@ -48,6 +47,8 @@ public class CtripUserServiceTest {
@Before
public void setUp() throws Exception {
when(serverConfigService.getValue("api.connectTimeout", "3000")).thenReturn("3000");
when(serverConfigService.getValue("api.readTimeout", "3000")).thenReturn("3000");
ctripUserService = new CtripUserService(serverConfigService);
ReflectionTestUtils.setField(ctripUserService, "restTemplate", restTemplate);
someResponseType =
......@@ -58,6 +59,7 @@ public class CtripUserServiceTest {
someUserServiceToken = "someToken";
when(serverConfigService.getValue("userService.url")).thenReturn(someUserServiceUrl);
when(serverConfigService.getValue("userService.accessToken")).thenReturn(someUserServiceToken);
}
@Test
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册