Merge remote-tracking branch 'upstream/develop' into merge-1.X-to-2.0

# Conflicts:
#	client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java
#	client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java
#	client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java
#	client/src/main/java/com/alibaba/nacos/client/naming/core/PushReceiver.java
#	console/src/main/resources/static/css/main.css
#	console/src/main/resources/static/js/main.js
#	naming/src/main/java/com/alibaba/nacos/naming/core/SubscribeManager.java
#	naming/src/test/java/com/alibaba/nacos/naming/core/SubscribeManagerTest.java
#	test/src/test/java/com/alibaba/nacos/test/BaseTest.java
This commit is contained in:
KomachiSion 2021-04-27 15:57:24 +08:00
commit 92da848375
60 changed files with 1815 additions and 146 deletions

View File

@ -64,11 +64,13 @@ public class PropertyKeyConst {
public static final String NAMING_CLIENT_BEAT_THREAD_COUNT = "namingClientBeatThreadCount";
public static final String NAMING_POLLING_THREAD_COUNT = "namingPollingThreadCount";
public static final String NAMING_REQUEST_DOMAIN_RETRY_COUNT = "namingRequestDomainMaxRetryCount";
public static final String NAMING_PUSH_EMPTY_PROTECTION = "namingPushEmptyProtection";
public static final String PUSH_RECEIVER_UDP_PORT = "push.receiver.udp.port";
/**
* Get the key value of some variable value from the system property.
*/

View File

@ -65,6 +65,8 @@ public class Constants {
public static final String CONFIG_TYPE = "Config-Type";
public static final String ENCRYPTED_DATA_KEY = "Encrypted-Data-Key";
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
public static final String SPACING_INTERVAL = "client-spacing-interval";

View File

@ -18,6 +18,9 @@ package com.alibaba.nacos.api.config;
import com.alibaba.nacos.api.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Config data type.
*
@ -60,7 +63,15 @@ public enum ConfigType {
*/
UNSET("unset");
String type;
private final String type;
private static final Map<String, ConfigType> LOCAL_MAP = new HashMap<String, ConfigType>();
static {
for (ConfigType configType : values()) {
LOCAL_MAP.put(configType.getType(), configType);
}
}
ConfigType(String type) {
this.type = type;
@ -84,11 +95,6 @@ public enum ConfigType {
if (StringUtils.isBlank(type)) {
return false;
}
for (ConfigType value : values()) {
if (value.type.equals(type)) {
return true;
}
}
return false;
return null != LOCAL_MAP.get(type) ? true : false;
}
}

View File

@ -34,7 +34,7 @@ public interface NacosConfigConverter<T> {
boolean canConvert(Class<T> targetType);
/**
* convert the Naocs's config of type S to target type T.
* Convert the Nacos' config of type S to target type T.
*
* @param config the Naocs's config to convert, which must be an instance of S (never {@code null})
* @return the converted object, which must be an instance of T (potentially {@code null})

View File

@ -14,19 +14,21 @@
* limitations under the License.
*/
package com.alibaba.nacos.address;
package com.alibaba.nacos.api.config.filter;
import com.alibaba.nacos.common.utils.IPUtil;
import org.junit.Test;
public class ParamCheckUtilTests {
/**
* Config Filter Interface default implementation.
*
* @author luyanbo(RobberPhex)
*/
public abstract class AbstractConfigFilter implements IConfigFilter {
@Test
public void checkIPs() {
String[] ips = {"127.0.0.1"};
System.out.println(IPUtil.checkIPs(ips));
String[] illlegalIps = {"127.100.19", "127.0.0.1"};
System.err.println(IPUtil.checkIPs(illlegalIps));
/**
* init.
*
* @param filterConfig Filter Config
*/
@Override
public void init(IFilterConfig filterConfig) {
}
}

View File

@ -23,7 +23,10 @@ import java.util.Properties;
/**
* Config Filter Interface.
*
* <p>DO NOT implement this interface directly, you should extend <code>AbstractConfigFilter</code>.
*
* @author Nacos
* @see AbstractConfigFilter
*/
public interface IConfigFilter {

View File

@ -23,6 +23,14 @@ package com.alibaba.nacos.api.config.filter;
*/
public interface IConfigRequest {
/**
* put param.
*
* @param key key
* @param value value
*/
void putParameter(String key, Object value);
/**
* get param.
*

View File

@ -44,6 +44,14 @@ public class ConfigFilterChainManager implements IConfigFilterChain {
}
}
public ConfigFilterChainManager(Properties properties) {
ServiceLoader<IConfigFilter> configFilters = ServiceLoader.load(IConfigFilter.class);
for (IConfigFilter configFilter : configFilters) {
configFilter.init(properties);
addFilter(configFilter);
}
}
/**
* Add filter.
*

View File

@ -73,6 +73,11 @@ public class ConfigRequest implements IConfigRequest {
param.put("type", type);
}
@Override
public void putParameter(String key, Object value) {
param.put(key, value);
}
@Override
public Object getParameter(String key) {
return param.get(key);

View File

@ -210,9 +210,13 @@ public class BeatReactor implements Closeable {
} catch (NacosException ex) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
} catch (Exception unknownEx) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, unknown exception msg: {}",
JacksonUtils.toJson(beatInfo), unknownEx.getMessage(), unknownEx);
} finally {
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
}

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.client.naming.core;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder;
import com.alibaba.nacos.common.lifecycle.Closeable;
@ -26,6 +27,7 @@ import com.alibaba.nacos.common.utils.ThreadUtils;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -52,10 +54,19 @@ public class PushReceiver implements Runnable, Closeable {
private volatile boolean closed = false;
public static String getPushReceiverUdpPort() {
return System.getenv(PropertyKeyConst.PUSH_RECEIVER_UDP_PORT);
}
public PushReceiver(ServiceInfoHolder serviceInfoHolder) {
try {
this.serviceInfoHolder = serviceInfoHolder;
this.udpSocket = new DatagramSocket();
String udpPort = getPushReceiverUdpPort();
if (StringUtils.isEmpty(udpPort)) {
this.udpSocket = new DatagramSocket();
} else {
this.udpSocket = new DatagramSocket(new InetSocketAddress(Integer.parseInt(udpPort)));
}
this.executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {

View File

@ -114,7 +114,8 @@ public class SecurityProxy {
return true;
}
}
} catch (Throwable ignore) {
} catch (Throwable throwable) {
SECURITY_LOGGER.warn("[SecurityProxy] login failed, error: ", throwable);
}
return false;

View File

@ -73,11 +73,7 @@ public class IPUtil {
* @return boolean
*/
public static boolean isIPv4(String addr) {
try {
return InetAddress.getByName(addr).getAddress().length == IPV4_ADDRESS_LENGTH;
} catch (UnknownHostException e) {
return false;
}
return ipv4Pattern.matcher(addr).matches();
}
/**
@ -101,12 +97,7 @@ public class IPUtil {
* @return boolean
*/
public static boolean isIP(String addr) {
try {
InetAddress.getByName(addr);
return true;
} catch (UnknownHostException e) {
return false;
}
return isIPv4(addr) || isIPv6(addr);
}
/**

View File

@ -76,7 +76,7 @@ public class MD5Utils {
}
/**
* 将一个字节数组转化为可见的字符串.
* Convert a byte array into a visible string.
*/
public static String encodeHexString(byte[] bytes) {
int l = bytes.length;

View File

@ -33,6 +33,7 @@ public class IPUtilTest {
Assert.assertFalse(IPUtil.isIPv4("[::1]"));
Assert.assertFalse(IPUtil.isIPv4("asdfasf"));
Assert.assertFalse(IPUtil.isIPv4("ffgertert"));
Assert.assertFalse(IPUtil.isIPv4("127.100.19"));
}
@Test
@ -47,6 +48,7 @@ public class IPUtilTest {
Assert.assertTrue(IPUtil.isIP("[::1]"));
Assert.assertTrue(IPUtil.isIP("127.0.0.1"));
Assert.assertFalse(IPUtil.isIP("er34234"));
Assert.assertFalse(IPUtil.isIP("127.100.19"));
}
@Test
@ -92,6 +94,15 @@ public class IPUtilTest {
checkSplitIPPortStr("[127.0.0.1]:88", true);
}
@Test
public void testCheckIPs() {
String[] ips = {"127.0.0.1"};
Assert.assertEquals("ok", IPUtil.checkIPs(ips));
String[] illegalIps = {"127.100.19", "127.0.0.1"};
Assert.assertEquals("illegal ip: 127.100.19", IPUtil.checkIPs(illegalIps));
}
/**
* checkSplitIpPortStr.
* 2020/9/4 14:12

View File

@ -135,6 +135,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -271,4 +271,8 @@ public class Constants {
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 = "/";
public static final String CONFIG_EXPORT_METADATA = ".meta.yml";
public static final String CONFIG_EXPORT_METADATA_NEW = ".metadata.yml";
}

View File

@ -31,6 +31,7 @@ import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo;
import com.alibaba.nacos.config.server.model.ConfigAllInfo;
import com.alibaba.nacos.config.server.model.ConfigInfo;
import com.alibaba.nacos.config.server.model.ConfigInfo4Beta;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus;
import com.alibaba.nacos.config.server.model.Page;
import com.alibaba.nacos.config.server.model.SameConfigPolicy;
@ -42,10 +43,12 @@ import com.alibaba.nacos.config.server.service.ConfigChangePublisher;
import com.alibaba.nacos.config.server.service.ConfigSubService;
import com.alibaba.nacos.config.server.service.repository.PersistService;
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.config.server.utils.MD5Util;
import com.alibaba.nacos.config.server.utils.ParamUtils;
import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
import com.alibaba.nacos.config.server.utils.ZipUtils;
import com.alibaba.nacos.sys.utils.InetUtils;
import org.apache.commons.lang3.StringUtils;
@ -79,6 +82,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -494,7 +498,7 @@ public class ConfigController {
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
}
if (metaData != null) {
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString()));
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA, metaData.toString()));
}
HttpHeaders headers = new HttpHeaders();
@ -505,6 +509,52 @@ public class ConfigController {
return new ResponseEntity<byte[]>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
}
/**
* new version export config add metadata.yml file record config metadata.
*
* @param dataId dataId string value.
* @param group group string value.
* @param appName appName string value.
* @param tenant tenant string value.
* @param ids id list value.
* @return ResponseEntity.
*/
@GetMapping(params = "exportV2=true")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public ResponseEntity<byte[]> exportConfigV2(@RequestParam(value = "dataId", required = false) String dataId,
@RequestParam(value = "group", required = false) String group,
@RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
@RequestParam(value = "ids", required = false) List<Long> ids) {
ids.removeAll(Collections.singleton(null));
tenant = NamespaceUtil.processNamespaceParameter(tenant);
List<ConfigAllInfo> dataList = persistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids);
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>();
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
for (ConfigAllInfo ci : dataList) {
ConfigMetadata.ConfigExportItem configMetadataItem = new ConfigMetadata.ConfigExportItem();
configMetadataItem.setAppName(ci.getAppName());
configMetadataItem.setDataId(ci.getDataId());
configMetadataItem.setDesc(ci.getDesc());
configMetadataItem.setGroup(ci.getGroup());
configMetadataItem.setType(ci.getType());
configMetadataItems.add(configMetadataItem);
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
}
ConfigMetadata configMetadata = new ConfigMetadata();
configMetadata.setMetadata(configMetadataItems);
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW,
YamlParserUtil.dumpObject(configMetadata)));
HttpHeaders headers = new HttpHeaders();
String fileName =
EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT)
+ EXPORT_CONFIG_FILE_NAME_EXT;
headers.add("Content-Disposition", "attachment;filename=" + fileName);
return new ResponseEntity<>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
}
/**
* Execute import and publish config operation.
*
@ -534,55 +584,23 @@ public class ConfigController {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
}
List<ConfigAllInfo> configInfoList = null;
List<Map<String, String>> unrecognizedList = null;
List<ConfigAllInfo> configInfoList = new ArrayList<>();
List<Map<String, String>> unrecognizedList = new ArrayList<>();
try {
ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes());
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
Map<String, String> metaDataMap = new HashMap<>(16);
if (metaDataZipItem != null) {
// 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) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
if (metaDataZipItem != null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(metaDataZipItem.getItemName())) {
// new export
RestResult<Map<String, Object>> errorResult = parseImportDataV2(unziped, configInfoList,
unrecognizedList, namespace);
if (errorResult != null) {
return errorResult;
}
}
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(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];
String tempDataId = dataId;
if (tempDataId.contains(".")) {
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
.substring(tempDataId.lastIndexOf(".") + 1);
}
final String metaDataId = group + "." + tempDataId + ".app";
ConfigAllInfo ci = new ConfigAllInfo();
ci.setTenant(namespace);
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(item.getItemData());
if (metaDataMap.get(metaDataId) != null) {
ci.setAppName(metaDataMap.get(metaDataId));
}
configInfoList.add(ci);
} else {
RestResult<Map<String, Object>> errorResult = parseImportData(unziped, configInfoList, unrecognizedList,
namespace);
if (errorResult != null) {
return errorResult;
}
}
} catch (IOException e) {
@ -590,7 +608,8 @@ public class ConfigController {
LOGGER.error("parsing data failed", e);
return RestResultUtils.buildResult(ResultCodeEnum.PARSING_DATA_FAILED, failedData);
}
if (configInfoList == null || configInfoList.isEmpty()) {
if (CollectionUtils.isEmpty(configInfoList)) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
}
@ -616,6 +635,152 @@ public class ConfigController {
return RestResultUtils.success("导入成功", saveResult);
}
/**
* old import config.
*
* @param unziped export file.
* @param configInfoList parse file result.
* @param unrecognizedList unrecognized file.
* @param namespace import namespace.
* @return error result.
*/
private RestResult<Map<String, Object>> parseImportData(ZipUtils.UnZipResult unziped,
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
Map<String, String> metaDataMap = new HashMap<>(16);
if (metaDataZipItem != null) {
// compatible all file separator
String metaDataStr = metaDataZipItem.getItemData().replaceAll("[\r\n]+", "|");
String[] metaDataArr = metaDataStr.split("\\|");
Map<String, Object> failedData = new HashMap<>(4);
for (String metaDataItem : metaDataArr) {
String[] metaDataItemArr = metaDataItem.split("=");
if (metaDataItemArr.length != 2) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
}
}
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
if (itemList != null && !itemList.isEmpty()) {
for (ZipUtils.ZipItem item : itemList) {
String[] groupAdnDataId = item.getItemName().split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
if (groupAdnDataId.length != 2) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", item.getItemName());
unrecognizedList.add(unrecognizedItem);
continue;
}
String group = groupAdnDataId[0];
String dataId = groupAdnDataId[1];
String tempDataId = dataId;
if (tempDataId.contains(".")) {
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
.substring(tempDataId.lastIndexOf(".") + 1);
}
final String metaDataId = group + "." + tempDataId + ".app";
ConfigAllInfo ci = new ConfigAllInfo();
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(item.getItemData());
if (metaDataMap.get(metaDataId) != null) {
ci.setAppName(metaDataMap.get(metaDataId));
}
ci.setTenant(namespace);
configInfoList.add(ci);
}
}
return null;
}
/**
* new version import config add .metadata.yml file.
*
* @param unziped export file.
* @param configInfoList parse file result.
* @param unrecognizedList unrecognized file.
* @param namespace import namespace.
* @return error result.
*/
private RestResult<Map<String, Object>> parseImportDataV2(ZipUtils.UnZipResult unziped,
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
ZipUtils.ZipItem metaDataItem = unziped.getMetaDataItem();
String metaData = metaDataItem.getItemData();
Map<String, Object> failedData = new HashMap<>(4);
ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaData, ConfigMetadata.class);
if (configMetadata == null || CollectionUtils.isEmpty(configMetadata.getMetadata())) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
List<ConfigMetadata.ConfigExportItem> configExportItems = configMetadata.getMetadata();
// check config metadata
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
if (StringUtils.isBlank(configExportItem.getDataId()) || StringUtils.isBlank(configExportItem.getGroup())
|| StringUtils.isBlank(configExportItem.getType())) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
}
List<ZipUtils.ZipItem> zipItemList = unziped.getZipItemList();
Set<String> metaDataKeys = configExportItems.stream()
.map(metaItem -> GroupKey.getKey(metaItem.getDataId(), metaItem.getGroup()))
.collect(Collectors.toSet());
Map<String, String> configContentMap = new HashMap<>(zipItemList.size());
int itemNameLength = 2;
zipItemList.forEach(item -> {
String itemName = item.getItemName();
String[] groupAdnDataId = itemName.split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
if (groupAdnDataId.length != itemNameLength) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", item.getItemName());
unrecognizedList.add(unrecognizedItem);
return;
}
String group = groupAdnDataId[0];
String dataId = groupAdnDataId[1];
String key = GroupKey.getKey(dataId, group);
// metadata does not contain config file
if (!metaDataKeys.contains(key)) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", "未在元数据中找到: " + item.getItemName());
unrecognizedList.add(unrecognizedItem);
return;
}
String itemData = item.getItemData();
configContentMap.put(key, itemData);
});
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
String dataId = configExportItem.getDataId();
String group = configExportItem.getGroup();
String content = configContentMap.get(GroupKey.getKey(dataId, group));
// config file not in metadata
if (content == null) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", "未在文件中找到: " + group + "/" + dataId);
unrecognizedList.add(unrecognizedItem);
continue;
}
ConfigAllInfo ci = new ConfigAllInfo();
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(content);
ci.setType(configExportItem.getType());
ci.setDesc(configExportItem.getDesc());
ci.setAppName(configExportItem.getAppName());
ci.setTenant(namespace);
configInfoList.add(ci);
}
return null;
}
/**
* Execute clone config operation.
*

View File

@ -19,6 +19,7 @@ package com.alibaba.nacos.config.server.filter;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.SmartSubscriber;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.model.event.RaftDbErrorEvent;
@ -113,6 +114,10 @@ public class CurcuitFilter implements Filter {
return;
}
final List<String> peers = (List<String>) ((ProtocolMetaData.ValueItem) o).getData();
if (CollectionUtils.isEmpty(peers)) {
isOpenService = false;
return;
}
final Member self = memberManager.getSelf();
final String raftAddress = self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT);
// Only when you are in the cluster and the current Leader is

View File

@ -0,0 +1,110 @@
/*
* 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.config.server.model;
import java.util.List;
import java.util.Objects;
/**
* config export Metadata.
*
* @author Nacos
*/
public class ConfigMetadata {
private List<ConfigExportItem> metadata;
public static class ConfigExportItem {
private String group;
private String dataId;
private String desc;
private String type;
private String appName;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigExportItem that = (ConfigExportItem) o;
return Objects.equals(group, that.group) && Objects.equals(dataId, that.dataId) && Objects
.equals(desc, that.desc) && Objects.equals(type, that.type) && Objects
.equals(appName, that.appName);
}
@Override
public int hashCode() {
return Objects.hash(group, dataId, desc, type, appName);
}
}
public List<ConfigExportItem> getMetadata() {
return metadata;
}
public void setMetadata(List<ConfigExportItem> metadata) {
this.metadata = metadata;
}
}

View File

@ -48,7 +48,7 @@ public enum ResultCodeEnum implements IResultCode {
DATA_EMPTY(100005, "导入的文件数据为空"),
NO_SELECTED_CONFIG(100006, "没有选择任何配");
NO_SELECTED_CONFIG(100006, "没有选择任何配");
private int code;

View File

@ -67,6 +67,7 @@ import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static com.alibaba.nacos.config.server.utils.LogUtil.DUMP_LOG;
import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG;
/**
@ -343,15 +344,25 @@ public abstract class DumpService {
dump(dataId, group, tenant, lastModified, handleIp, false);
}
/**
* Add DumpTask to TaskManager, it will execute asynchronously.
*/
public void dump(String dataId, String group, String tenant, long lastModified, String handleIp, boolean isBeta) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta));
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
}
/**
* Add DumpTask to TaskManager, it will execute asynchronously.
*/
public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
boolean isBeta) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta), tag);
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
}
public void dumpAll() {

View File

@ -406,7 +406,7 @@ public class DistributedDatabaseOperateImpl extends RequestProcessor4CP implemen
}
return RestResultUtils.success();
} catch (Throwable ex) {
LogUtil.DEFAULT_LOG.error("data import has error : {}", ex);
LogUtil.DEFAULT_LOG.error("data import has error :", ex);
return RestResultUtils.failed(ex.getMessage());
}
});

View File

@ -0,0 +1,141 @@
/*
* 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.config.server.utils;
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.config.server.model.ConfigMetadata;
import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* YamlParserUtil.
*
* @author Nacos
*/
public class YamlParserUtil {
private static Yaml yaml;
static {
Representer representer = new Representer() {
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
Tag customTag) {
if (propertyValue == null) {
return null;
} else {
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
}
}
};
yaml = new Yaml(new YamlParserConstructor(), representer);
}
/**
* Serialize a Java object into a YAML string.
*
* @param object Java object.
* @return YAML string.
*/
public static String dumpObject(Object object) {
return yaml.dumpAsMap(object);
}
/**
* Parse YAML String and produce the corresponding Java object (Standard Java classes and in YamlParserConstructor
* specified Construct).
*
* @param content YAML String
* @param type Java object.
* @param <T> Java object type.
* @return Java object.
*/
public static <T> T loadObject(String content, Class<T> type) {
return yaml.loadAs(content, type);
}
public static class YamlParserConstructor extends SafeConstructor {
public static Tag configMetadataTag = new Tag(ConfigMetadata.class);
public YamlParserConstructor() {
super();
yamlConstructors.put(configMetadataTag, new ConstructYamlConfigMetadata());
}
}
public static class ConstructYamlConfigMetadata extends AbstractConstruct {
@Override
public Object construct(Node node) {
if (!YamlParserConstructor.configMetadataTag.getValue().equals(node.getTag().getValue())) {
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
"could not determine a constructor for the tag " + node.getTag() + node.getStartMark());
}
MappingNode mNode = (MappingNode) node;
List<NodeTuple> value = mNode.getValue();
if (CollectionUtils.isEmpty(value)) {
return null;
}
NodeTuple nodeTuple = value.get(0);
ConfigMetadata configMetadata = new ConfigMetadata();
SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode();
if (CollectionUtils.isEmpty(sequenceNode.getValue())) {
return configMetadata;
}
List<ConfigMetadata.ConfigExportItem> exportItems = sequenceNode.getValue().stream().map(itemValue -> {
ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem();
MappingNode itemMap = (MappingNode) itemValue;
List<NodeTuple> propertyValues = itemMap.getValue();
Map<String, String> metadataMap = new HashMap<>(propertyValues.size());
propertyValues.forEach(metadata -> {
ScalarNode keyNode = (ScalarNode) metadata.getKeyNode();
ScalarNode valueNode = (ScalarNode) metadata.getValueNode();
metadataMap.put(keyNode.getValue(), valueNode.getValue());
});
configExportItem.setDataId(metadataMap.get("dataId"));
configExportItem.setGroup(metadataMap.get("group"));
configExportItem.setType(metadataMap.get("type"));
configExportItem.setDesc(metadataMap.get("desc"));
configExportItem.setAppName(metadataMap.get("appName"));
return configExportItem;
}).collect(Collectors.toList());
configMetadata.setMetadata(exportItems);
return configMetadata;
}
}
}

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.config.server.utils;
import com.alibaba.nacos.config.server.constant.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -134,7 +135,11 @@ public class ZipUtils {
out.write(buffer, 0, offset);
}
String entryName = entry.getName();
if (".meta.yml".equals(entryName)) {
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA.equals(entryName)) {
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
continue;
}
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(entryName)) {
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
continue;
}

View File

@ -0,0 +1,97 @@
/*
* 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.config.server.utils;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.yaml.snakeyaml.constructor.ConstructorException;
import java.util.ArrayList;
import java.util.List;
public class YamlParserUtilTest {
private static final String CONFIG_METADATA_STRING =
"metadata:\n" + "- dataId: testData1\n" + " group: testGroup1\n" + " type: text\n"
+ "- appName: testAppName\n" + " dataId: testData2\n" + " desc: test desc\n"
+ " group: testGroup2\n" + " type: yaml\n";
private ConfigMetadata.ConfigExportItem item1;
private ConfigMetadata.ConfigExportItem item2;
@Before
public void setUp() {
item1 = new ConfigMetadata.ConfigExportItem();
item1.setDataId("testData1");
item1.setGroup("testGroup1");
item1.setType("text");
item2 = new ConfigMetadata.ConfigExportItem();
item2.setDataId("testData2");
item2.setGroup("testGroup2");
item2.setType("yaml");
item2.setAppName("testAppName");
item2.setDesc("test desc");
}
@Test
public void testDumpObject() {
ConfigMetadata configMetadata = new ConfigMetadata();
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
configMetadataItems.add(item1);
configMetadataItems.add(item2);
configMetadata.setMetadata(configMetadataItems);
String parseString = YamlParserUtil.dumpObject(configMetadata);
Assert.assertEquals(CONFIG_METADATA_STRING, parseString);
}
@Test
public void testLoadObject() {
ConfigMetadata configMetadata = YamlParserUtil.loadObject(CONFIG_METADATA_STRING, ConfigMetadata.class);
Assert.assertNotNull(configMetadata);
List<ConfigMetadata.ConfigExportItem> metadataList = configMetadata.getMetadata();
Assert.assertNotNull(metadataList);
Assert.assertEquals(metadataList.size(), 2);
ConfigMetadata.ConfigExportItem configExportItem1 = metadataList.get(0);
ConfigMetadata.ConfigExportItem configExportItem2 = metadataList.get(1);
Assert.assertEquals(configExportItem1, item1);
Assert.assertEquals(configExportItem2, item2);
}
@Test(expected = ConstructorException.class)
public void testNotSupportType() {
YamlParserUtil.loadObject("name: test", YamlTest.class);
}
private static class YamlTest {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -102,7 +102,6 @@ class DiffEditorDialog extends React.Component {
)}
</div>
);
console.log(footer);
return (
<div>
<Dialog

View File

@ -285,6 +285,7 @@ const I18N_CONF = {
application: 'Application',
operation: 'Operation',
export: 'Export query results',
newExport: 'New version export query results',
import: 'Import',
uploadBtn: 'Upload File',
importSucc: 'The import was successful',
@ -310,6 +311,7 @@ const I18N_CONF = {
unrecognizedEntries: 'Unrecognized entries',
skippedEntries: 'skipped entries',
exportSelected: 'Export selected configs',
newExportSelected: 'New version export selected configs',
clone: 'Clone',
exportSelectedAlertTitle: 'Export config',
exportSelectedAlertContent: 'please select the configuration to export',

View File

@ -283,6 +283,7 @@ const I18N_CONF = {
application: '归属应用:',
operation: '操作',
export: '导出查询结果',
newExport: '新版导出查询结果',
import: '导入配置',
uploadBtn: '上传文件',
importSucc: '导入成功',
@ -307,6 +308,7 @@ const I18N_CONF = {
unrecognizedEntries: '未识别的条目',
skippedEntries: '跳过的条目',
exportSelected: '导出选中的配置',
newExportSelected: '新版导出选中的配置',
clone: '克隆',
exportSelectedAlertTitle: '配置导出',
exportSelectedAlertContent: '请选择要导出的配置',

View File

@ -59,6 +59,7 @@ class ConfigDetail extends React.Component {
checkedBeta: false,
switchEncrypt: false,
tag: [],
editorClass: 'editor-normal',
};
this.field = new Field(this);
this.dataId = getParams('dataId') || 'yanlin';
@ -78,6 +79,7 @@ class ConfigDetail extends React.Component {
componentDidMount() {
this.initData();
this.getDataDetail();
this.initFullScreenEvent();
}
initData() {
@ -90,6 +92,22 @@ class ConfigDetail extends React.Component {
this.setState({ tag: [{ title: locale.official, key: 'normal' }] });
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
openLoading() {
this.setState({
loading: true,
@ -262,7 +280,7 @@ class ConfigDetail extends React.Component {
render() {
const { locale = {} } = this.props;
const { configCompareVisible } = this.state;
const { configCompareVisible, editorClass } = this.state;
const { init } = this.field;
const formItemLayout = {
labelCol: {
@ -347,7 +365,7 @@ class ConfigDetail extends React.Component {
<Input htmlType={'text'} readOnly {...init('md5')} />
</FormItem>
<FormItem label={locale.configuration} required {...formItemLayout}>
<div style={{ clear: 'both', height: 300 }} id="container" />
<div className={editorClass} id="container" />
</FormItem>
</Form>
<Row>

View File

@ -19,4 +19,17 @@
margin-left: 1em;
font-size: 14px;
}
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}

View File

@ -369,7 +369,7 @@ class ConfigEditor extends React.Component {
appName: this.inApp ? this.edasAppId : this.field.getValue('appName'),
group: this.field.getValue('group'),
desc: this.field.getValue('desc'),
config_tags: this.state.config_tags.join(),
config_tags: this.state.config_tags.join(','),
type: this.state.configType,
content,
tenant: this.tenant,

View File

@ -85,7 +85,9 @@ class ConfigEditor extends React.Component {
type: 'text', // 配置格式
},
tagDataSource: [],
subscriberDataSource: [],
openAdvancedSettings: false,
editorClass: 'editor-normal',
};
this.successDialog = React.createRef();
this.diffEditorDialog = React.createRef();
@ -113,6 +115,7 @@ class ConfigEditor extends React.Component {
betaPublishSuccess: true,
});
});
this.getSubscribesByNamespace();
}
);
} else {
@ -121,6 +124,7 @@ class ConfigEditor extends React.Component {
}
this.initMoacoEditor('text', '');
}
this.initFullScreenEvent();
});
}
@ -153,6 +157,22 @@ class ConfigEditor extends React.Component {
}
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
createDiffCodeMirror(leftCode, rightCode) {
const target = this.diffEditorDialog.current.getInstance();
target.innerHTML = '';
@ -238,6 +258,10 @@ class ConfigEditor extends React.Component {
Object.keys(form).forEach(key => {
payload[key] = form[key];
});
let configTags = this.state.form.config_tags;
if (configTags.length > 0) {
payload.config_tags = configTags.join(',');
}
const stringify = require('qs/lib/stringify');
this.setState({ loading: true });
return request({
@ -308,18 +332,20 @@ class ConfigEditor extends React.Component {
setConfigTags(tags) {
const { tagDataSource } = this.state;
const lastTag = tags[tags.length - 1];
if (tagDataSource.indexOf(lastTag) < 0) {
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
}
if (tags.length > 5) {
tags.pop();
}
tags.forEach((v, i) => {
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
tags.splice(i, 1);
if (tags.length > 0) {
const lastTag = tags[tags.length - 1];
if (tagDataSource.indexOf(lastTag) < 0) {
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
}
});
if (tags.length > 5) {
tags.pop();
}
tags.forEach((v, i) => {
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
tags.splice(i, 1);
}
});
}
this.changeForm({ config_tags: tags });
}
@ -365,6 +391,31 @@ class ConfigEditor extends React.Component {
this.changeForm({ ...form, config_tags: configTags ? configTags.split(',') : [] });
this.initMoacoEditor(type, content);
this.codeVal = content;
this.setState({
tagDataSource: this.state.form.config_tags,
});
return res;
});
}
getSubscribesByNamespace() {
const namespace = getParams('namespace');
const { dataId, group } = this.state.form;
const params = {
dataId,
group,
namespaceId: namespace,
tenant: namespace,
};
// get subscribes of the namespace
return request.get('v1/cs/configs/listener', { params }).then(res => {
const { subscriberDataSource } = this.state;
const lisentersGroupkeyIpMap = res.lisentersGroupkeyStatus;
if (lisentersGroupkeyIpMap) {
this.setState({
subscriberDataSource: subscriberDataSource.concat(Object.keys(lisentersGroupkeyIpMap)),
});
}
return res;
});
}
@ -407,6 +458,8 @@ class ConfigEditor extends React.Component {
tabActiveKey,
dataIdError = {},
groupError = {},
subscriberDataSource,
editorClass,
} = this.state;
const { locale = {} } = this.props;
@ -493,11 +546,16 @@ class ConfigEditor extends React.Component {
</Checkbox>
)}
{isBeta && (
<Input.TextArea
aria-label="TextArea"
placeholder="127.0.0.1,127.0.0.2"
value={betaIps}
onChange={betaIps => this.setState({ betaIps })}
<Select
size="medium"
hasArrow
autoWidth
mode="tag"
filterLocal
dataSource={subscriberDataSource}
onChange={betaIps => this.setState({ betaIps: betaIps.join(',') })}
hasClear
value={betaIps ? betaIps.split(',') : []}
/>
)}
</Form.Item>
@ -535,7 +593,7 @@ class ConfigEditor extends React.Component {
</div>
}
>
<div style={{ clear: 'both', height: 300 }} id="container" />
<div id="container" className={editorClass} />
</Form.Item>
</Form>
<Row>

View File

@ -73,4 +73,16 @@
font-size: 14px;
}
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}
}

View File

@ -527,7 +527,21 @@ class ConfigurationManagement extends React.Component {
});
}
exportSelectedData() {
exportDataNew() {
const { group, appName, dataId, openUri } = this;
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
openUri('v1/cs/configs', {
exportV2: 'true',
tenant: getParams('namespace'),
group,
appName,
dataId,
ids: '',
accessToken,
});
}
exportSelectedData(newVersion) {
const ids = [];
const { locale = {} } = this.props;
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
@ -539,14 +553,25 @@ class ConfigurationManagement extends React.Component {
return;
}
configsTableSelected.forEach((value, key, map) => ids.push(key));
this.openUri('v1/cs/configs', {
export: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
if (newVersion) {
this.openUri('v1/cs/configs', {
exportV2: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
} else {
this.openUri('v1/cs/configs', {
export: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
}
}
multipleSelectionDeletion() {
@ -1164,6 +1189,16 @@ class ConfigurationManagement extends React.Component {
{locale.export}
</Button>
</Form.Item>
<Form.Item label={''}>
<Button
type={'primary'}
style={{ marginRight: 10 }}
onClick={this.exportDataNew.bind(this)}
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
>
{locale.newExport}
</Button>
</Form.Item>
<Form.Item label={''}>
<Button
type={'primary'}
@ -1255,7 +1290,12 @@ class ConfigurationManagement extends React.Component {
{
text: locale.exportSelected,
locaid: 'configsExport',
onClick: () => this.exportSelectedData(),
onClick: () => this.exportSelectedData(false),
},
{
text: locale.newExportSelected,
locaid: 'configsExport',
onClick: () => this.exportSelectedData(true),
},
{
text: locale.clone,

View File

@ -80,6 +80,7 @@ class NewConfig extends React.Component {
encrypt: false,
addonBefore: '',
showGroupWarning: false,
editorClass: 'editor-normal',
};
this.codeValue = '';
this.mode = 'text';
@ -99,6 +100,7 @@ class NewConfig extends React.Component {
} else {
this.initMoacoEditor();
}
this.initFullScreenEvent();
}
changeModel(type) {
@ -138,6 +140,22 @@ class NewConfig extends React.Component {
});
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
setGroup(value) {
this.group = value || '';
this.field.setValue('group', this.group);
@ -435,6 +453,7 @@ class NewConfig extends React.Component {
label: 'Properties',
},
];
const { editorClass } = this.state;
return (
<div style={{ padding: 10 }}>
@ -575,7 +594,7 @@ class NewConfig extends React.Component {
}
required
>
<div id={'container'} style={{ width: '100%', height: 300 }} />
<div id={'container'} className={editorClass} />
</FormItem>
<FormItem label=" ">

View File

@ -21,3 +21,17 @@
.more-item.hide {
display: none;
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -220,7 +220,7 @@ public class JRaftServer {
createMultiRaftGroup(processors);
Loggers.RAFT.info("========= The raft protocol start finished... =========");
} catch (Exception e) {
Loggers.RAFT.error("raft protocol start failure, error : {}", e);
Loggers.RAFT.error("raft protocol start failure, cause: ", e);
throw new JRaftException(e);
}
}
@ -412,7 +412,7 @@ public class JRaftServer {
Loggers.RAFT.info("========= The raft protocol has been closed =========");
} catch (Throwable t) {
Loggers.RAFT.error("There was an error in the raft protocol shutdown, error : {}", t);
Loggers.RAFT.error("There was an error in the raft protocol shutdown, cause: ", t);
}
}

View File

@ -102,7 +102,7 @@ public class JRaftUtils {
DiskUtils.forceMkdir(new File(snapshotUri));
DiskUtils.forceMkdir(new File(metaDataUri));
} catch (Exception e) {
Loggers.RAFT.error("Init Raft-File dir have some error : {}", e);
Loggers.RAFT.error("Init Raft-File dir have some error, cause: ", e);
throw new RuntimeException(e);
}

View File

@ -32,6 +32,8 @@ import java.util.concurrent.TimeUnit;
/**
* Nacos naming example.
* <p>Add the JVM parameter to run the NamingExample:</p>
* {@code -DserverAddr=${nacos.server.ip}:${nacos.server.port} -Dnamespace=${namespaceId}}
*
* @author nkorange
*/

View File

@ -24,6 +24,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Optional;
/**
* Detect and control the working status of local server.
@ -68,6 +69,10 @@ public class ServerStatusManager {
return serverStatus;
}
public Optional<String> getErrorMsg() {
return consistencyService.getErrorMsg();
}
public class ServerStatusUpdater implements Runnable {
@Override

View File

@ -19,6 +19,8 @@ package com.alibaba.nacos.naming.consistency;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.naming.pojo.Record;
import java.util.Optional;
/**
* Consistence service for all implementations to derive.
*
@ -77,6 +79,13 @@ public interface ConsistencyService {
*/
void unListen(String key, RecordListener listener) throws NacosException;
/**
* Get the error message of the consistency protocol.
*
* @return the consistency protocol error message.
*/
Optional<String> getErrorMsg();
/**
* Tell the status of this consistency service.
*

View File

@ -23,6 +23,8 @@ import com.alibaba.nacos.naming.pojo.Record;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* Consistency delegate.
*
@ -81,6 +83,25 @@ public class DelegateConsistencyServiceImpl implements ConsistencyService {
return ephemeralConsistencyService.isAvailable() && persistentConsistencyService.isAvailable();
}
@Override
public Optional<String> getErrorMsg() {
String errorMsg;
if (ephemeralConsistencyService.getErrorMsg().isPresent()
&& persistentConsistencyService.getErrorMsg().isPresent()) {
errorMsg = "'" + ephemeralConsistencyService.getErrorMsg().get() + "' in Distro protocol and '"
+ persistentConsistencyService.getErrorMsg().get() + "' in jRaft protocol";
} else if (ephemeralConsistencyService.getErrorMsg().isPresent()
&& !persistentConsistencyService.getErrorMsg().isPresent()) {
errorMsg = ephemeralConsistencyService.getErrorMsg().get();
} else if (!ephemeralConsistencyService.getErrorMsg().isPresent()
&& persistentConsistencyService.getErrorMsg().isPresent()) {
errorMsg = persistentConsistencyService.getErrorMsg().get();
} else {
errorMsg = null;
}
return Optional.ofNullable(errorMsg);
}
private ConsistencyService mapConsistencyService(String key) {
return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}

View File

@ -50,6 +50,7 @@ import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
@ -356,6 +357,17 @@ public class DistroConsistencyServiceImpl implements EphemeralConsistencyService
return isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus());
}
@Override
public Optional<String> getErrorMsg() {
String errorMsg;
if (!isInitialized() && !ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus())) {
errorMsg = "Distro protocol is not initialized";
} else {
errorMsg = null;
}
return Optional.ofNullable(errorMsg);
}
public boolean isInitialized() {
return distroProtocol.isInitialized() || !globalConfig.isDataWarmup();
}

View File

@ -28,6 +28,8 @@ import com.alibaba.nacos.naming.pojo.Record;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* Persistent consistency service delegate.
*
@ -89,6 +91,11 @@ public class PersistentConsistencyServiceDelegateImpl implements PersistentConsi
return switchOne().isAvailable();
}
@Override
public Optional<String> getErrorMsg() {
return switchOne().getErrorMsg();
}
private PersistentConsistencyService switchOne() {
return switchNewPersistentService ? newPersistentConsistencyService : oldPersistentConsistencyService;
}

View File

@ -92,6 +92,8 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP
*/
protected volatile boolean hasError = false;
protected volatile String jRaftErrorMsg;
/**
* If use old raft, should not notify listener even new listener add.
*/
@ -211,6 +213,7 @@ public abstract class BasePersistentServiceProcessor extends RequestProcessor4CP
public void onError(Throwable error) {
super.onError(error);
hasError = true;
jRaftErrorMsg = error.getMessage();
}
protected Type getDatumTypeFromKey(String key) {

View File

@ -40,6 +40,7 @@ import com.google.protobuf.ByteString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
@ -158,4 +159,19 @@ public class PersistentServiceProcessor extends BasePersistentServiceProcessor {
public boolean isAvailable() {
return hasLeader && !hasError;
}
@Override
public Optional<String> getErrorMsg() {
String errorMsg;
if (hasLeader && hasError) {
errorMsg = "The raft peer is in error: " + jRaftErrorMsg;
} else if (hasLeader && !hasError) {
errorMsg = null;
} else if (!hasLeader && hasError) {
errorMsg = "Could not find leader! And the raft peer is in error: " + jRaftErrorMsg;
} else {
errorMsg = "Could not find leader!";
}
return Optional.ofNullable(errorMsg);
}
}

View File

@ -31,6 +31,7 @@ import com.google.protobuf.ByteString;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* Persistent service manipulation layer in stand-alone mode.
@ -107,4 +108,15 @@ public class StandalonePersistentServiceProcessor extends BasePersistentServiceP
public boolean isAvailable() {
return !hasError;
}
@Override
public Optional<String> getErrorMsg() {
String errorMsg;
if (hasError) {
errorMsg = "The raft peer is in error: " + jRaftErrorMsg;
} else {
errorMsg = null;
}
return Optional.ofNullable(errorMsg);
}
}

View File

@ -33,6 +33,7 @@ import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Optional;
/**
* Use simplified Raft protocol to maintain the consistency status of Nacos cluster.
@ -54,6 +55,8 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
private volatile boolean stopWork = false;
private String errorMsg;
public RaftConsistencyServiceImpl(ClusterVersionJudgement versionJudgement, RaftCore raftCore,
SwitchDomain switchDomain) {
this.raftCore = raftCore;
@ -127,6 +130,17 @@ public class RaftConsistencyServiceImpl implements PersistentConsistencyService
return raftCore.isInitialized() || ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus());
}
@Override
public Optional<String> getErrorMsg() {
String errorMsg;
if (!raftCore.isInitialized() && !ServerStatus.UP.name().equals(switchDomain.getOverriddenServerStatus())) {
errorMsg = "The old raft protocol node is not initialized";
} else {
errorMsg = null;
}
return Optional.ofNullable(errorMsg);
}
/**
* Put a new datum from other server.
*

View File

@ -330,8 +330,8 @@ public class ServiceController {
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
public ObjectNode subscribers(HttpServletRequest request) {
int pageNo = NumberUtils.toInt(WebUtils.required(request, "pageNo"));
int pageSize = NumberUtils.toInt(WebUtils.required(request, "pageSize"));
int pageNo = NumberUtils.toInt(WebUtils.optional(request, "pageNo", "1"));
int pageSize = NumberUtils.toInt(WebUtils.optional(request, "pageSize", "1000"));
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
@ -341,7 +341,7 @@ public class ServiceController {
ObjectNode result = JacksonUtils.createEmptyJsonNode();
try {
List<Subscriber> subscribers = subscribeManager.getSubscribers(serviceName, namespaceId, aggregation);
List<Subscriber> subscribers = subscribeManager.getSubscribers(serviceName, namespaceId, aggregation, pageNo, pageSize);
int start = (pageNo - 1) * pageSize;
if (start < 0) {

View File

@ -55,6 +55,8 @@ public class SubscribeManager {
* @param serviceName service name
* @param namespaceId namespace id
* @param aggregation aggregation
* @param pageNo 页码
* @param pageSize 页大小
* @return list of subscriber
*/
public List<Subscriber> getSubscribers(String serviceName, String namespaceId, boolean aggregation) {

View File

@ -97,8 +97,12 @@ public class TrafficReviseFilter implements Filter {
return;
}
resp.getWriter()
.write("server is " + serverStatusManager.getServerStatus().name() + " now, please try again later!");
final String statusMsg = "server is " + serverStatusManager.getServerStatus().name() + "now";
if (serverStatusManager.getErrorMsg().isPresent()) {
resp.getWriter().write(statusMsg + ", detailed error message: " + serverStatusManager.getErrorMsg());
} else {
resp.getWriter().write(statusMsg + ", please try again later!");
}
resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.naming.consistency;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.naming.consistency.ephemeral.EphemeralConsistencyService;
import com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl;
import com.alibaba.nacos.naming.pojo.Record;
import junit.framework.TestCase;
import org.junit.Before;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class DelegateConsistencyServiceImplTest extends TestCase {
private DelegateConsistencyServiceImpl delegateConsistencyService;
@Mock
private PersistentConsistencyServiceDelegateImpl persistentConsistencyService;
@Mock
private EphemeralConsistencyService ephemeralConsistencyService;
private static final String EPHEMERAL_KEY_PREFIX = "ephemeral.";
public static final String INSTANCE_LIST_KEY_PREFIX = "com.alibaba.nacos.naming.iplist.";
private final String ephemeralPrefix = INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX;
@Mock
private Record record;
private String ephemeralKey;
private String peristentKey;
public DelegateConsistencyServiceImplTest() {
}
@Before
public void setUp() {
delegateConsistencyService
= new DelegateConsistencyServiceImpl(persistentConsistencyService, ephemeralConsistencyService);
ephemeralKey = ephemeralPrefix + "test-key";
peristentKey = "persistent-test-key";
}
@Test
public void testPut() throws NacosException {
delegateConsistencyService.put(ephemeralKey, record);
verify(ephemeralConsistencyService).put(ephemeralKey, record);
verify(persistentConsistencyService, never()).put(ephemeralKey, record);
delegateConsistencyService.put(peristentKey, record);
verify(persistentConsistencyService).put(peristentKey, record);
}
@Test
public void testRemove() throws NacosException {
delegateConsistencyService.remove(ephemeralKey);
verify(ephemeralConsistencyService).remove(ephemeralKey);
verify(persistentConsistencyService, never()).remove(ephemeralKey);
delegateConsistencyService.remove(peristentKey);
verify(persistentConsistencyService).remove(peristentKey);
}
@Test
public void testGet() throws NacosException {
delegateConsistencyService.get(ephemeralKey);
verify(ephemeralConsistencyService).get(ephemeralKey);
verify(persistentConsistencyService, never()).get(ephemeralKey);
delegateConsistencyService.get(peristentKey);
verify(persistentConsistencyService).get(peristentKey);
}
@Test
public void testListen() throws NacosException {
delegateConsistencyService.listen(ephemeralKey, null);
verify(ephemeralConsistencyService).listen(ephemeralKey, null);
verify(persistentConsistencyService, never()).listen(ephemeralKey, null);
delegateConsistencyService.listen(peristentKey, null);
verify(persistentConsistencyService).listen(peristentKey, null);
}
@Test
public void testUnListen() throws NacosException {
delegateConsistencyService.unListen(ephemeralKey, null);
verify(ephemeralConsistencyService).unListen(ephemeralKey, null);
verify(persistentConsistencyService, never()).unListen(ephemeralKey, null);
delegateConsistencyService.unListen(peristentKey, null);
verify(persistentConsistencyService).unListen(peristentKey, null);
}
@Test
public void testIsAvailable() {
delegateConsistencyService.isAvailable();
verify(ephemeralConsistencyService).isAvailable();
verify(persistentConsistencyService, never()).isAvailable();
}
@Test
public void testGetErrorMsg() {
int ephemeralCalledTimes = 3;
delegateConsistencyService.getErrorMsg();
verify(ephemeralConsistencyService, times(ephemeralCalledTimes)).getErrorMsg();
verify(persistentConsistencyService).getErrorMsg();
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.naming.consistency.persistent;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.core.distributed.ProtocolManager;
import com.alibaba.nacos.naming.consistency.RecordListener;
import com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor;
import com.alibaba.nacos.naming.consistency.persistent.raft.RaftConsistencyServiceImpl;
import com.alibaba.nacos.naming.pojo.Record;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
@RunWith(MockitoJUnitRunner.class)
public class PersistentConsistencyServiceDelegateImplTest {
@Mock
private ClusterVersionJudgement clusterVersionJudgement;
@Mock
private RaftConsistencyServiceImpl raftConsistencyService;
@Mock
private ProtocolManager protocolManager;
@Mock
private Record record;
@Mock
private RecordListener recordListener;
@Mock
private BasePersistentServiceProcessor basePersistentServiceProcessor;
private PersistentConsistencyServiceDelegateImpl oldPersistentConsistencyServiceDelegate;
private PersistentConsistencyServiceDelegateImpl newPersistentConsistencyServiceDelegate;
@Before
public void setUp() throws Exception {
oldPersistentConsistencyServiceDelegate = new PersistentConsistencyServiceDelegateImpl(clusterVersionJudgement,
raftConsistencyService, protocolManager);
newPersistentConsistencyServiceDelegate = new PersistentConsistencyServiceDelegateImpl(clusterVersionJudgement,
raftConsistencyService, protocolManager);
Class<PersistentConsistencyServiceDelegateImpl> persistentConsistencyServiceDelegateClass = PersistentConsistencyServiceDelegateImpl.class;
Field switchNewPersistentService = persistentConsistencyServiceDelegateClass
.getDeclaredField("switchNewPersistentService");
switchNewPersistentService.setAccessible(true);
switchNewPersistentService.set(newPersistentConsistencyServiceDelegate, true);
Field newPersistentConsistencyService = persistentConsistencyServiceDelegateClass
.getDeclaredField("newPersistentConsistencyService");
newPersistentConsistencyService.setAccessible(true);
newPersistentConsistencyService.set(newPersistentConsistencyServiceDelegate, basePersistentServiceProcessor);
}
@Test()
public void testPut() throws Exception {
String key = "record_key";
oldPersistentConsistencyServiceDelegate.put(key, record);
Mockito.verify(raftConsistencyService).put(key, record);
newPersistentConsistencyServiceDelegate.put(key, record);
Mockito.verify(basePersistentServiceProcessor).put(key, record);
}
@Test
public void testRemove() throws NacosException {
String key = "record_key";
oldPersistentConsistencyServiceDelegate.remove(key);
Mockito.verify(raftConsistencyService).remove(key);
newPersistentConsistencyServiceDelegate.remove(key);
Mockito.verify(basePersistentServiceProcessor).remove(key);
}
@Test()
public void testGet() throws NacosException {
String key = "record_key";
oldPersistentConsistencyServiceDelegate.get(key);
Mockito.verify(raftConsistencyService).get(key);
newPersistentConsistencyServiceDelegate.get(key);
Mockito.verify(basePersistentServiceProcessor).get(key);
}
@Test
public void testListen() throws NacosException {
String key = "listen_key";
oldPersistentConsistencyServiceDelegate.listen(key, recordListener);
Mockito.verify(raftConsistencyService).listen(key, recordListener);
newPersistentConsistencyServiceDelegate.listen(key, recordListener);
Mockito.verify(basePersistentServiceProcessor).listen(key, recordListener);
}
@Test
public void testUnListen() throws NacosException {
String key = "listen_key";
oldPersistentConsistencyServiceDelegate.unListen(key, recordListener);
Mockito.verify(raftConsistencyService).unListen(key, recordListener);
newPersistentConsistencyServiceDelegate.unListen(key, recordListener);
Mockito.verify(basePersistentServiceProcessor).unListen(key, recordListener);
}
@Test
public void testIsAvailable() {
oldPersistentConsistencyServiceDelegate.isAvailable();
Mockito.verify(raftConsistencyService).isAvailable();
newPersistentConsistencyServiceDelegate.isAvailable();
Mockito.verify(basePersistentServiceProcessor).isAvailable();
}
@Test
public void testGetErrorMsg() {
oldPersistentConsistencyServiceDelegate.getErrorMsg();
Mockito.verify(raftConsistencyService).getErrorMsg();
newPersistentConsistencyServiceDelegate.getErrorMsg();
Mockito.verify(basePersistentServiceProcessor).getErrorMsg();
}
}

View File

@ -121,7 +121,7 @@
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
<maven-failsafe-plugin.version>2.19.1</maven-failsafe-plugin.version>
<maven-assembly-plugin.version>3.0.0</maven-assembly-plugin.version>
<maven-checkstyle-plugin.version>3.1.1</maven-checkstyle-plugin.version>
<maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
<maven-flatten-version>1.1.0</maven-flatten-version>
<!-- dependency version related to plugin -->
<extra-enforcer-rules.version>1.0-beta-4</extra-enforcer-rules.version>
@ -131,7 +131,7 @@
<spring-boot-dependencies.version>2.1.17.RELEASE</spring-boot-dependencies.version>
<servlet-api.version>3.0</servlet-api.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-io.version>2.2</commons-io.version>
<commons-io.version>2.7</commons-io.version>
<commons-collections.version>3.2.2</commons-collections.version>
<commons-logging.version>1.2</commons-logging.version>
<commons-dbcp.version>1.4</commons-dbcp.version>
@ -152,7 +152,7 @@
<jjwt.version>0.11.2</jjwt.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>
<guava.version>30.1-jre</guava.version>
<javatuples.version>1.2</javatuples.version>
<commonOkHttp.version>0.4.1</commonOkHttp.version>
<grpc-java.version>1.24.0</grpc-java.version>

View File

@ -540,7 +540,7 @@ public final class DiskUtils {
}
@Override
public void close() {
public void close() throws IOException {
target.close();
}

View File

@ -22,6 +22,10 @@ import com.alibaba.nacos.client.config.http.MetricsHttpAgent;
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
import com.alibaba.nacos.config.server.utils.ZipUtils;
import com.alibaba.nacos.test.base.ConfigCleanUtils;
import com.fasterxml.jackson.databind.JsonNode;
@ -46,7 +50,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @author klw
@ -147,6 +150,24 @@ 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", "test1");
params.put("group", "TEST_IMPORT2");
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", "test3");
params.put("group", "TEST_IMPORT2");
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", "test4");
params.put("group", "TEST_IMPORT2");
params.put("beta", "false");
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params, agent.getEncode(), TIME_OUT);
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.getCode());
} catch (Exception e) {
Assert.fail();
}
@ -344,4 +365,171 @@ public class ConfigExportAndImportAPI_CITCase {
}
return group + "." + tempDataId + ".app";
}
@Test
public void testExportV2() {
String dataId = "testNoAppname2.txt";
String getDataUrl =
"?search=accurate&group=TEST1_GROUP&pageNo=1&pageSize=10&tenant=&namespaceId=&dataId="+dataId;
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
JsonNode resultObj = JacksonUtils.toObj(queryResult);
JsonNode resultConfigs = resultObj.get("pageItems");
JsonNode config1 = resultConfigs.get(0);
String configId = config1.get("id").asText();
String exportByIdsUrl = "?exportV2=true&tenant=&group=&appName=&ids=" + configId;
byte[] zipData = httpClient.download(SERVER_ADDR + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
Assert.assertEquals(1, zipItemList.size());
String config1Name = config1.get("group").textValue() + "/" + config1.get("dataId").textValue();
for (ZipUtils.ZipItem zipItem : zipItemList) {
if (!(config1Name.equals(zipItem.getItemName()))) {
Assert.fail();
}
}
Assert.assertEquals(dataId, config1.get("dataId").asText());
String group = config1.get("group").asText();
String queryConfigDetailResult = httpClient
.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + "?show=all&dataId=" + dataId + "&group=" + group, null);
JsonNode configDetailResult = JacksonUtils.toObj(queryConfigDetailResult);
Assert.assertNotNull(configDetailResult);
// verification metadata
ZipUtils.ZipItem metaDataItem = unZiped.getMetaDataItem();
Assert.assertNotNull(metaDataItem);
String metaDataItemItemData = metaDataItem.getItemData();
ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaDataItemItemData, ConfigMetadata.class);
Assert.assertNotNull(configMetadata);
Assert.assertEquals(configMetadata.getMetadata().size(), 1);
ConfigMetadata.ConfigExportItem config1Metadata = new ConfigMetadata.ConfigExportItem();
config1Metadata.setDataId(dataId);
config1Metadata.setGroup(group);
config1Metadata.setType(configDetailResult.get("type").asText());
config1Metadata.setAppName(configDetailResult.get("appName") == null ? null : configDetailResult.get("appName").asText());
config1Metadata.setDesc(configDetailResult.get("desc") == null ? null : configDetailResult.get("desc").asText());
ConfigMetadata.ConfigExportItem configExportItem1 = configMetadata.getMetadata().get(0);
Assert.assertEquals(configExportItem1, config1Metadata);
}
@Test
public void testImportV2() {
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test1", "test: test1"));
String metaDataStr = "metadata:\n" + "- appName: testAppName\n" + " dataId: test1\n"
+ " desc: testDesc\n" + " group: TEST_IMPORT2\n" + " type: yaml";
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
String importUrl = "?import=true&namespace=";
Map<String, String> importPrarm = new HashMap<>(1);
importPrarm.put("policy", "OVERWRITE");
UploadByteFile uploadByteFile = new UploadByteFile();
uploadByteFile.setFileName("testImport.zip");
uploadByteFile.setFileBytes(ZipUtils.zip(zipItemList));
uploadByteFile.setMediaType("application/zip");
uploadByteFile.setPrarmName("file");
String importResult = httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
Collections.singletonList(uploadByteFile), null);
JsonNode importResObj = JacksonUtils.toObj(importResult);
Assert.assertEquals(importResObj.get("data").get("succCount").asInt(), 1);
String queryConfigDetailResult = httpClient
.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + "?show=all&dataId=test1&group=TEST_IMPORT2", null);
JsonNode configDetailResult = JacksonUtils.toObj(queryConfigDetailResult);
Assert.assertNotNull(configDetailResult);
Assert.assertEquals(configDetailResult.get("dataId").asText(), "test1");
Assert.assertEquals(configDetailResult.get("group").asText(), "TEST_IMPORT2");
Assert.assertEquals(configDetailResult.get("type").asText(), "yaml");
Assert.assertEquals(configDetailResult.get("appName").asText(), "testAppName");
Assert.assertEquals(configDetailResult.get("desc").asText(), "testDesc");
}
@Test
public void testImportV2MetadataError() {
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test2", "test: test2"));
String metaDataStr = "metadata:\n" + "- appName: testAppName\n" + " desc: test desc\n"
+ " group: TEST_IMPORT\n" + " type: yaml";
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
String importUrl = "?import=true&namespace=";
Map<String, String> importPrarm = new HashMap<>(1);
importPrarm.put("policy", "OVERWRITE");
UploadByteFile uploadByteFile = new UploadByteFile();
uploadByteFile.setFileName("testImport.zip");
uploadByteFile.setFileBytes(ZipUtils.zip(zipItemList));
uploadByteFile.setMediaType("application/zip");
uploadByteFile.setPrarmName("file");
String importResult = httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
Collections.singletonList(uploadByteFile), null);
JsonNode importResObj = JacksonUtils.toObj(importResult);
Assert.assertEquals(importResObj.get("code").intValue(), ResultCodeEnum.METADATA_ILLEGAL.getCode());
Assert.assertEquals(importResObj.get("message").textValue(), ResultCodeEnum.METADATA_ILLEGAL.getCodeMsg());
}
@Test
public void testImportV2MetadataNotFind() {
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test3.yml", "test: test3"));
String metaDataStr = "metadata:\n" + "- dataId: notExist\n" + " group: TEST_IMPORT2\n" + " type: yaml\n"
+ "- dataId: test3.yml\n" + " group: TEST_IMPORT2\n" + " type: yaml";
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
String importUrl = "?import=true&namespace=";
Map<String, String> importPrarm = new HashMap<>(1);
importPrarm.put("policy", "OVERWRITE");
UploadByteFile uploadByteFile = new UploadByteFile();
uploadByteFile.setFileName("testImport.zip");
uploadByteFile.setFileBytes(ZipUtils.zip(zipItemList));
uploadByteFile.setMediaType("application/zip");
uploadByteFile.setPrarmName("file");
String importResult = httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
Collections.singletonList(uploadByteFile), null);
JsonNode importResObj = JacksonUtils.toObj(importResult);
JsonNode data = importResObj.get("data");
Assert.assertEquals(1, data.get("succCount").intValue());
// test unrecognizedData
int unrecognizedCount = data.get("unrecognizedCount").intValue();
Assert.assertEquals(1, unrecognizedCount);
JsonNode unrecognizedData = data.get("unrecognizedData").get(0);
Assert.assertEquals("未在文件中找到: TEST_IMPORT2/notExist", unrecognizedData.get("itemName").textValue());
}
@Test
public void testImportV2ConfigIgnore() {
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/test4", "test: test4"));
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT2/ignore.yml", "test: test4"));
String metaDataStr = "metadata:\n" + "- appName: testAppName\n" + " dataId: test4\n"
+ " desc: testDesc\n" + " group: TEST_IMPORT2\n" + " type: yaml";
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, metaDataStr));
String importUrl = "?import=true&namespace=";
Map<String, String> importPrarm = new HashMap<>(1);
importPrarm.put("policy", "OVERWRITE");
UploadByteFile uploadByteFile = new UploadByteFile();
uploadByteFile.setFileName("testImport.zip");
uploadByteFile.setFileBytes(ZipUtils.zip(zipItemList));
uploadByteFile.setMediaType("application/zip");
uploadByteFile.setPrarmName("file");
String importResult = httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm,
Collections.singletonList(uploadByteFile), null);
JsonNode importResObj = JacksonUtils.toObj(importResult);
JsonNode data = importResObj.get("data");
Assert.assertEquals(1, data.get("succCount").intValue());
// test unrecognizedData
int unrecognizedCount = data.get("unrecognizedCount").intValue();
Assert.assertEquals(1, unrecognizedCount);
JsonNode unrecognizedData = data.get("unrecognizedData").get(0);
Assert.assertEquals("未在元数据中找到: TEST_IMPORT2/ignore.yml", unrecognizedData.get("itemName").textValue());
}
}

View File

@ -53,7 +53,9 @@ public class HttpUtils_ITCase {
public void test_rest_result() throws Exception {
String json = "{\"code\":200,\"message\":null,\"data\":[{\"USERNAME\":\"nacos\",\"PASSWORD\":\"$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu\",\"ENABLED\":true}]}";
RestResult<Object> result = ResponseHandler.convert(json, new GenericType<RestResult<Object>>(){}.getType());
System.out.println(result);
Assert.assertEquals(200, result.getCode());
Assert.assertNull(result.getMessage());
Assert.assertNotNull(result.getData());
}
@Test