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:
commit
92da848375
@ -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.
|
||||
*/
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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})
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ public enum ResultCodeEnum implements IResultCode {
|
||||
|
||||
DATA_EMPTY(100005, "导入的文件数据为空"),
|
||||
|
||||
NO_SELECTED_CONFIG(100006, "没有选择任何配制");
|
||||
NO_SELECTED_CONFIG(100006, "没有选择任何配置");
|
||||
|
||||
private int code;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -102,7 +102,6 @@ class DiffEditorDialog extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
console.log(footer);
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
|
@ -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',
|
||||
|
@ -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: '请选择要导出的配置',
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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=" ">
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
6
pom.xml
6
pom.xml
@ -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>
|
||||
|
@ -540,7 +540,7 @@ public final class DiskUtils {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
public void close() throws IOException {
|
||||
target.close();
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user