Merge branch 'develop' into feature_address_server
This commit is contained in:
commit
7f97289340
@ -19,8 +19,7 @@ matrix:
|
||||
env: CUSTOM_JDK="oraclejdk8"
|
||||
|
||||
jdk:
|
||||
- openjdk10
|
||||
- openjdk9
|
||||
- openjdk11
|
||||
- openjdk8
|
||||
|
||||
before_install:
|
||||
|
@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -17,9 +17,9 @@ package com.alibaba.nacos.client.config.utils;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@ -33,7 +33,7 @@ public class MD5 {
|
||||
private static int DIGITS_SIZE = 16;
|
||||
private static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
private static Map<Character, Integer> rDigits = new HashMap<Character, Integer>(16);
|
||||
private static Map<Character, Integer> rDigits = Maps.newHashMapWithExpectedSize(16);
|
||||
|
||||
static {
|
||||
for (int i = 0; i < digits.length; ++i) {
|
||||
|
@ -29,7 +29,6 @@ import com.alibaba.nacos.api.selector.AbstractSelector;
|
||||
import com.alibaba.nacos.api.selector.ExpressionSelector;
|
||||
import com.alibaba.nacos.api.selector.SelectorType;
|
||||
import com.alibaba.nacos.client.config.impl.SpasAdapter;
|
||||
import com.alibaba.nacos.client.identify.Constants;
|
||||
import com.alibaba.nacos.client.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.client.naming.beat.BeatInfo;
|
||||
import com.alibaba.nacos.client.naming.utils.*;
|
||||
@ -218,6 +217,7 @@ public class NamingProxy {
|
||||
params.put("ip", instance.getIp());
|
||||
params.put("port", String.valueOf(instance.getPort()));
|
||||
params.put("weight", String.valueOf(instance.getWeight()));
|
||||
params.put("enabled", String.valueOf(instance.isEnabled()));
|
||||
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
|
||||
params.put("metadata", JSON.toJSONString(instance.getMetadata()));
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -18,6 +18,8 @@ package com.alibaba.nacos.config.server.controller;
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.exception.NacosException;
|
||||
import com.alibaba.nacos.config.server.model.*;
|
||||
import com.alibaba.nacos.config.server.result.ResultBuilder;
|
||||
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
|
||||
import com.alibaba.nacos.config.server.service.AggrWhitelist;
|
||||
import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent;
|
||||
import com.alibaba.nacos.config.server.service.ConfigSubService;
|
||||
@ -25,15 +27,21 @@ import com.alibaba.nacos.config.server.service.PersistService;
|
||||
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
||||
import com.alibaba.nacos.config.server.utils.*;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
||||
import com.google.common.base.Joiner;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -41,8 +49,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static com.alibaba.nacos.core.utils.SystemUtils.LOCAL_IP;
|
||||
|
||||
@ -57,6 +64,14 @@ public class ConfigController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ConfigController.class);
|
||||
|
||||
private static final String NAMESPACE_PUBLIC_KEY = "public";
|
||||
|
||||
public static final String EXPORT_CONFIG_FILE_NAME = "nacos_config_export_";
|
||||
|
||||
public static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip";
|
||||
|
||||
public static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private final transient ConfigServletInner inner;
|
||||
|
||||
private final transient PersistService persistService;
|
||||
@ -382,4 +397,192 @@ public class ConfigController {
|
||||
return rr;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(params = "export=true", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public ResponseEntity<byte[]> exportConfig(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@RequestParam("group") 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));
|
||||
String idsStr = Joiner.on(",").join(ids);
|
||||
List<ConfigInfo> dataList = persistService.findAllConfigInfo4Export(group, tenant, appName, idsStr);
|
||||
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>();
|
||||
StringBuilder metaData = null;
|
||||
for(ConfigInfo ci : dataList){
|
||||
if(StringUtils.isNotBlank(ci.getAppName())){
|
||||
// Handle appName
|
||||
if(metaData == null){
|
||||
metaData = new StringBuilder();
|
||||
}
|
||||
String metaDataId = ci.getDataId();
|
||||
if(metaDataId.contains(".")){
|
||||
metaDataId = metaDataId.substring(0,metaDataId.lastIndexOf("."))
|
||||
+ "~" + metaDataId.substring(metaDataId.lastIndexOf(".") + 1);
|
||||
}
|
||||
metaData.append(ci.getGroup()).append(".").append(metaDataId).append(".app=")
|
||||
// Fixed use of "\r\n" here
|
||||
.append(ci.getAppName()).append("\r\n");
|
||||
}
|
||||
String itemName = ci.getGroup() + "/" + ci.getDataId() ;
|
||||
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
|
||||
}
|
||||
if(metaData != null){
|
||||
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString()));
|
||||
}
|
||||
|
||||
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<byte[]>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(params = "import=true", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public RestResult<Map<String, Object>> importAndPublishConfig(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||
@RequestParam(value = "namespace", required = false) String namespace,
|
||||
@RequestParam(value = "policy", defaultValue = "ABORT")
|
||||
SameConfigPolicy policy,
|
||||
MultipartFile file) throws NacosException {
|
||||
Map<String, Object> failedData = new HashMap<>(4);
|
||||
|
||||
if(StringUtils.isNotBlank(namespace)){
|
||||
if(persistService.tenantInfoCountByTenantId(namespace) <= 0){
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
||||
}
|
||||
}
|
||||
List<ConfigInfo> configInfoList = null;
|
||||
try {
|
||||
ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes());
|
||||
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
|
||||
Map<String, String> metaDataMap = new HashMap<>(16);
|
||||
if(metaDataZipItem != null){
|
||||
String metaDataStr = metaDataZipItem.getItemData();
|
||||
String[] metaDataArr = metaDataStr.split("\r\n");
|
||||
for(String metaDataItem : metaDataArr){
|
||||
String[] metaDataItemArr = metaDataItem.split("=");
|
||||
if(metaDataItemArr.length != 2){
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
|
||||
}
|
||||
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
|
||||
}
|
||||
}
|
||||
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
|
||||
if(itemList != null && !itemList.isEmpty()){
|
||||
configInfoList = new ArrayList<>(itemList.size());
|
||||
for(ZipUtils.ZipItem item : itemList){
|
||||
String[] groupAdnDataId = item.getItemName().split("/");
|
||||
if(!item.getItemName().contains("/") || groupAdnDataId.length != 2){
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.DATA_VALIDATION_FAILED, failedData);
|
||||
}
|
||||
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);
|
||||
}
|
||||
String metaDataId = group + "." + tempDataId + ".app";
|
||||
ConfigInfo ci = new ConfigInfo();
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
failedData.put("succCount", 0);
|
||||
log.error("parsing data failed", e);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.PARSING_DATA_FAILED, failedData);
|
||||
}
|
||||
if (configInfoList == null || configInfoList.isEmpty()) {
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
|
||||
}
|
||||
final String srcIp = RequestUtil.getRemoteIp(request);
|
||||
String requestIpApp = RequestUtil.getAppName(request);
|
||||
final Timestamp time = TimeUtils.getCurrentTime();
|
||||
Map<String, Object> saveResult = persistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp,
|
||||
null, time, false, policy);
|
||||
for (ConfigInfo configInfo : configInfoList) {
|
||||
EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(),
|
||||
configInfo.getTenant(), time.getTime()));
|
||||
ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
|
||||
configInfo.getTenant(), requestIpApp, time.getTime(),
|
||||
LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
||||
}
|
||||
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
||||
}
|
||||
|
||||
@RequestMapping(params = "clone=true", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@RequestParam(value = "src_user", required = false) String srcUser,
|
||||
@RequestParam(value = "tenant", required = true) String namespace,
|
||||
@RequestParam(value = "ids", required = true) List<Long> ids,
|
||||
@RequestParam(value = "policy", defaultValue = "ABORT")
|
||||
SameConfigPolicy policy) throws NacosException {
|
||||
Map<String, Object> failedData = new HashMap<>(4);
|
||||
|
||||
if(NAMESPACE_PUBLIC_KEY.equals(namespace.toLowerCase())){
|
||||
namespace = "";
|
||||
} else if(persistService.tenantInfoCountByTenantId(namespace) <= 0){
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
||||
}
|
||||
|
||||
ids.removeAll(Collections.singleton(null));
|
||||
String idsStr = Joiner.on(",").join(ids);
|
||||
List<ConfigInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, idsStr);
|
||||
|
||||
if(queryedDataList == null || queryedDataList.isEmpty()){
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
|
||||
}
|
||||
|
||||
List<ConfigInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
|
||||
|
||||
for(ConfigInfo ci : queryedDataList){
|
||||
ConfigInfo ci4save = new ConfigInfo();
|
||||
ci4save.setTenant(namespace);
|
||||
ci4save.setGroup(ci.getGroup());
|
||||
ci4save.setDataId(ci.getDataId());
|
||||
ci4save.setContent(ci.getContent());
|
||||
if(StringUtils.isNotBlank(ci.getAppName())){
|
||||
ci4save.setAppName(ci.getAppName());
|
||||
}
|
||||
configInfoList4Clone.add(ci4save);
|
||||
}
|
||||
|
||||
if (configInfoList4Clone.isEmpty()) {
|
||||
failedData.put("succCount", 0);
|
||||
return ResultBuilder.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
|
||||
}
|
||||
final String srcIp = RequestUtil.getRemoteIp(request);
|
||||
String requestIpApp = RequestUtil.getAppName(request);
|
||||
final Timestamp time = TimeUtils.getCurrentTime();
|
||||
Map<String, Object> saveResult = persistService.batchInsertOrUpdate(configInfoList4Clone, srcUser, srcIp,
|
||||
null, time, false, policy);
|
||||
for (ConfigInfo configInfo : configInfoList4Clone) {
|
||||
EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(),
|
||||
configInfo.getTenant(), time.getTime()));
|
||||
ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
|
||||
configInfo.getTenant(), requestIpApp, time.getTime(),
|
||||
LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
||||
}
|
||||
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.enums;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @ClassName: FileTypeEnum
|
||||
* @Description: config file type enum
|
||||
* @date 2019/7/1 10:21
|
||||
*/
|
||||
public enum FileTypeEnum {
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: yaml file
|
||||
*/
|
||||
YML("yaml"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: yaml file
|
||||
*/
|
||||
YAML("yaml"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: text file
|
||||
*/
|
||||
TXT("text"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: text file
|
||||
*/
|
||||
TEXT("text"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: json file
|
||||
*/
|
||||
JSON("json"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: xml file
|
||||
*/
|
||||
XML("xml"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: html file
|
||||
*/
|
||||
HTM("html"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: html file
|
||||
*/
|
||||
HTML("html"),
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: properties file
|
||||
*/
|
||||
PROPERTIES("properties")
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: file type corresponding to file extension
|
||||
*/
|
||||
private String fileType;
|
||||
|
||||
FileTypeEnum(String fileType){
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public String getFileType(){
|
||||
return this.fileType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @ClassName: SameConfigPolicy
|
||||
* @Description: processing policy of the same configuration
|
||||
* @date 2019/5/21 10:55
|
||||
*/
|
||||
public enum SameConfigPolicy {
|
||||
|
||||
/**
|
||||
* @Description: abort import on duplicate
|
||||
*/
|
||||
ABORT,
|
||||
|
||||
/**
|
||||
* @Description: skipping on duplicate
|
||||
*/
|
||||
SKIP,
|
||||
|
||||
/**
|
||||
* @Description: overwrite on duplicate
|
||||
*/
|
||||
OVERWRITE
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.result;
|
||||
|
||||
import com.alibaba.nacos.config.server.model.RestResult;
|
||||
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
|
||||
import com.alibaba.nacos.config.server.result.core.IResultCode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @ClassName: ResultBuilder
|
||||
* @Description: util for generating com.alibaba.nacos.config.server.model.RestResult
|
||||
* @date 2019/6/28 14:47
|
||||
*/
|
||||
public class ResultBuilder {
|
||||
|
||||
public static <T extends Object> RestResult<T> buildResult(IResultCode resultCode, T resultData){
|
||||
Assert.notNull(resultCode, "the resultCode can not be null");
|
||||
RestResult<T> rr = new RestResult<>(resultCode.getCode(), resultCode.getCodeMsg(), resultData);
|
||||
return rr;
|
||||
}
|
||||
|
||||
public static <T extends Object> RestResult<T> buildSuccessResult(T resultData){
|
||||
return buildResult(ResultCodeEnum.SUCCESS, resultData);
|
||||
}
|
||||
|
||||
public static <T extends Object> RestResult<T> buildSuccessResult(String successMsg, T resultData){
|
||||
RestResult<T> rr = buildResult(ResultCodeEnum.SUCCESS, resultData);
|
||||
rr.setMessage(successMsg);
|
||||
return rr;
|
||||
}
|
||||
|
||||
public static <T extends Object> RestResult<T> buildSuccessResult(){
|
||||
return buildResult(ResultCodeEnum.SUCCESS, null);
|
||||
}
|
||||
|
||||
public static <T extends Object> RestResult<T> buildSuccessResult(String successMsg){
|
||||
RestResult<T> rr = buildResult(ResultCodeEnum.SUCCESS, null);
|
||||
rr.setMessage(successMsg);
|
||||
return rr;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.result.code;
|
||||
|
||||
import com.alibaba.nacos.config.server.result.core.IResultCode;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @ClassName: ResultCodeEnum
|
||||
* @Description: result code enum
|
||||
* @date 2019/6/28 14:43
|
||||
*/
|
||||
public enum ResultCodeEnum implements IResultCode {
|
||||
|
||||
/** common code **/
|
||||
SUCCESS(200, "处理成功"),
|
||||
ERROR(500, "服务器内部错误"),
|
||||
|
||||
/** config use 100001 ~ 100999 **/
|
||||
NAMESPACE_NOT_EXIST(100001, "目标 namespace 不存在"),
|
||||
|
||||
METADATA_ILLEGAL(100002, "导入的元数据非法"),
|
||||
|
||||
DATA_VALIDATION_FAILED(100003, "未读取到合法数据"),
|
||||
|
||||
PARSING_DATA_FAILED(100004, "解析数据失败"),
|
||||
|
||||
DATA_EMPTY(100005, "导入的文件数据为空"),
|
||||
|
||||
|
||||
|
||||
;
|
||||
|
||||
private int code;
|
||||
|
||||
private String msg;
|
||||
|
||||
ResultCodeEnum(int code, String codeMsg){
|
||||
this.code = code;
|
||||
this.msg = codeMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCodeMsg() {
|
||||
return msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.result.core;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @ClassName: IResultCode
|
||||
* @Description: result code enum needs to be implemented this interface
|
||||
* @date 2019/6/28 14:44
|
||||
*/
|
||||
public interface IResultCode {
|
||||
|
||||
/**
|
||||
* get the result code
|
||||
*
|
||||
* @author klw
|
||||
* @Date 2019/6/28 14:56
|
||||
* @Param []
|
||||
* @return java.lang.String
|
||||
*/
|
||||
int getCode();
|
||||
|
||||
/**
|
||||
* get the result code's message
|
||||
*
|
||||
* @author klw
|
||||
* @Date 2019/6/28 14:56
|
||||
* @Param []
|
||||
* @return java.lang.String
|
||||
*/
|
||||
String getCodeMsg();
|
||||
}
|
@ -15,25 +15,15 @@
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.service;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.alibaba.nacos.config.server.enums.FileTypeEnum;
|
||||
import com.alibaba.nacos.config.server.exception.NacosException;
|
||||
import com.alibaba.nacos.config.server.model.*;
|
||||
import com.alibaba.nacos.config.server.utils.LogUtil;
|
||||
import com.alibaba.nacos.config.server.utils.MD5;
|
||||
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||
import com.alibaba.nacos.config.server.utils.ParamUtils;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
@ -53,12 +43,17 @@ import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.alibaba.nacos.config.server.utils.LogUtil;
|
||||
import com.alibaba.nacos.config.server.utils.MD5;
|
||||
import com.alibaba.nacos.config.server.utils.PaginationHelper;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.defaultLog;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.fatalLog;
|
||||
|
||||
/**
|
||||
* 数据库服务,提供ConfigInfo在数据库的存取<br> 3.0开始增加数据版本号, 并将物理删除改为逻辑删除<br> 3.0增加数据库切换功能
|
||||
@ -76,6 +71,16 @@ public class PersistService {
|
||||
|
||||
private DataSourceService dataSourceService;
|
||||
|
||||
private static final String SQL_FIND_ALL_CONFIG_INFO = "select data_id,group_id,tenant_id,app_name,content,type from config_info";
|
||||
|
||||
private static final String SQL_TENANT_INFO_COUNT_BY_TENANT_ID = "select count(1) from tenant_info where tenant_id = ?";
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: constant variables
|
||||
*/
|
||||
public static final String SPOT = ".";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
dataSourceService = dynamicDataSource.getDataSource();
|
||||
@ -1476,7 +1481,6 @@ public class PersistService {
|
||||
*
|
||||
* @param pageNo 页码(必须大于0)
|
||||
* @param pageSize 每页大小(必须大于0)
|
||||
* @param group
|
||||
* @return ConfigInfo对象的集合
|
||||
*/
|
||||
public Page<ConfigInfo> findConfigInfoByApp(final int pageNo,
|
||||
@ -3288,6 +3292,145 @@ public class PersistService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* query all configuration information according to group, appName, tenant (for export)
|
||||
*
|
||||
* @param group
|
||||
* @return Collection of ConfigInfo objects
|
||||
*/
|
||||
public List<ConfigInfo> findAllConfigInfo4Export(final String group, final String tenant,
|
||||
final String appName, final String ids) {
|
||||
String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
|
||||
StringBuilder where = new StringBuilder(" where ");
|
||||
List<String> paramList = new ArrayList<>();
|
||||
if(StringUtils.isNotBlank(ids)){
|
||||
where.append(" id in (").append(ids).append(") ");
|
||||
} else {
|
||||
where.append(" tenant_id=? ");
|
||||
paramList.add(tenantTmp);
|
||||
if (StringUtils.isNotBlank(group)) {
|
||||
where.append(" and group_id=? ");
|
||||
paramList.add(group);
|
||||
}
|
||||
if (StringUtils.isNotBlank(appName)) {
|
||||
where.append(" and app_name=? ");
|
||||
paramList.add(appName);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return this.jt.query(SQL_FIND_ALL_CONFIG_INFO + where, paramList.toArray(), CONFIG_INFO_ROW_MAPPER);
|
||||
} catch (CannotGetJdbcConnectionException e) {
|
||||
fatalLog.error("[db-error] " + e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* batch operation,insert or update
|
||||
* the format of the returned:
|
||||
* succCount: number of successful imports
|
||||
* skipCount: number of import skips (only with skip for the same configs)
|
||||
* failData: import failed data (only with abort for the same configs)
|
||||
* skipData: data skipped at import (only with skip for the same configs)
|
||||
*/
|
||||
public Map<String, Object> batchInsertOrUpdate(List<ConfigInfo> configInfoList, String srcUser, String srcIp,
|
||||
Map<String, Object> configAdvanceInfo, Timestamp time, boolean notify, SameConfigPolicy policy) throws NacosException {
|
||||
int succCount = 0;
|
||||
int skipCount = 0;
|
||||
List<Map<String, String>> failData = null;
|
||||
List<Map<String, String>> skipData = null;
|
||||
|
||||
for (int i = 0; i < configInfoList.size(); i++) {
|
||||
ConfigInfo configInfo = configInfoList.get(i);
|
||||
try {
|
||||
ParamUtils.checkParam(configInfo.getDataId(), configInfo.getGroup(), "datumId", configInfo.getContent());
|
||||
} catch (NacosException e) {
|
||||
defaultLog.error("data verification failed", e);
|
||||
throw e;
|
||||
}
|
||||
ConfigInfo configInfo2Save = new ConfigInfo(configInfo.getDataId(), configInfo.getGroup(),
|
||||
configInfo.getTenant(), configInfo.getAppName(), configInfo.getContent());
|
||||
|
||||
// simple judgment of file type based on suffix
|
||||
String type = null;
|
||||
if (configInfo.getDataId().contains(SPOT)) {
|
||||
String extName = configInfo.getDataId().substring(configInfo.getDataId().lastIndexOf(SPOT) + 1).toLowerCase();
|
||||
try{
|
||||
type = FileTypeEnum.valueOf(extName).getFileType();
|
||||
}catch (Exception ex){
|
||||
type = FileTypeEnum.TEXT.getFileType();
|
||||
}
|
||||
}
|
||||
if (configAdvanceInfo == null) {
|
||||
configAdvanceInfo = new HashMap<>(16);
|
||||
}
|
||||
configAdvanceInfo.put("type", type);
|
||||
try {
|
||||
addConfigInfo(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify);
|
||||
succCount++;
|
||||
} catch (DataIntegrityViolationException ive) {
|
||||
// uniqueness constraint conflict
|
||||
if (SameConfigPolicy.ABORT.equals(policy)) {
|
||||
failData = new ArrayList<>();
|
||||
skipData = new ArrayList<>();
|
||||
Map<String, String> faileditem = new HashMap<>(2);
|
||||
faileditem.put("dataId", configInfo2Save.getDataId());
|
||||
faileditem.put("group", configInfo2Save.getGroup());
|
||||
failData.add(faileditem);
|
||||
for (int j = (i + 1); j < configInfoList.size(); j++) {
|
||||
ConfigInfo skipConfigInfo = configInfoList.get(j);
|
||||
Map<String, String> skipitem = new HashMap<>(2);
|
||||
skipitem.put("dataId", skipConfigInfo.getDataId());
|
||||
skipitem.put("group", skipConfigInfo.getGroup());
|
||||
skipData.add(skipitem);
|
||||
}
|
||||
break;
|
||||
} else if (SameConfigPolicy.SKIP.equals(policy)) {
|
||||
skipCount++;
|
||||
if (skipData == null) {
|
||||
skipData = new ArrayList<>();
|
||||
}
|
||||
Map<String, String> skipitem = new HashMap<>(2);
|
||||
skipitem.put("dataId", configInfo2Save.getDataId());
|
||||
skipitem.put("group", configInfo2Save.getGroup());
|
||||
skipData.add(skipitem);
|
||||
} else if (SameConfigPolicy.OVERWRITE.equals(policy)) {
|
||||
succCount++;
|
||||
updateConfigInfo(configInfo2Save, srcIp, srcUser, time, configAdvanceInfo, notify);
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<String, Object> result = new HashMap<>(4);
|
||||
result.put("succCount", succCount);
|
||||
result.put("skipCount", skipCount);
|
||||
if (failData != null && !failData.isEmpty()) {
|
||||
result.put("failData", failData);
|
||||
}
|
||||
if (skipData != null && !skipData.isEmpty()) {
|
||||
result.put("skipData", skipData);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* query tenantInfo (namespace) existence based by tenantId
|
||||
*
|
||||
* @param tenantId
|
||||
* @return count by tenantId
|
||||
*/
|
||||
public int tenantInfoCountByTenantId(String tenantId) {
|
||||
Assert.hasText(tenantId, "tenantId can not be null");
|
||||
List<String> paramList = new ArrayList<>();
|
||||
paramList.add(tenantId);
|
||||
Integer result = this.jt.queryForObject(SQL_TENANT_INFO_COUNT_BY_TENANT_ID, paramList.toArray(), Integer.class);
|
||||
if (result == null) {
|
||||
return 0;
|
||||
}
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
|
||||
static final TenantInfoRowMapper TENANT_INFO_ROW_MAPPER = new TenantInfoRowMapper();
|
||||
|
||||
static final UserRowMapper USER_ROW_MAPPER = new UserRowMapper();
|
||||
|
@ -16,11 +16,14 @@
|
||||
package com.alibaba.nacos.config.server.service.notify;
|
||||
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent;
|
||||
import com.alibaba.nacos.config.server.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.config.server.service.ConfigDataChangeEvent;
|
||||
import com.alibaba.nacos.config.server.service.ServerListService;
|
||||
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
||||
import com.alibaba.nacos.config.server.utils.*;
|
||||
import com.alibaba.nacos.config.server.utils.LogUtil;
|
||||
import com.alibaba.nacos.config.server.utils.PropertyUtil;
|
||||
import com.alibaba.nacos.config.server.utils.RunningConfigUtils;
|
||||
import com.alibaba.nacos.config.server.utils.StringUtils;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher.AbstractEventListener;
|
||||
import com.alibaba.nacos.config.server.utils.event.EventDispatcher.Event;
|
||||
import org.apache.http.HttpResponse;
|
||||
@ -68,7 +71,7 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
|
||||
// 并发产生 ConfigDataChangeEvent
|
||||
if (event instanceof ConfigDataChangeEvent) {
|
||||
ConfigDataChangeEvent evt = (ConfigDataChangeEvent)event;
|
||||
ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event;
|
||||
long dumpTs = evt.lastModifiedTs;
|
||||
String dataId = evt.dataId;
|
||||
String group = evt.group;
|
||||
@ -79,9 +82,9 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
// 其实这里任何类型队列都可以
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
for (int i = 0; i < ipList.size(); i++) {
|
||||
queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String)ipList.get(i), evt.isBeta));
|
||||
queue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, (String) ipList.get(i), evt.isBeta));
|
||||
}
|
||||
EXCUTOR.execute(new AsyncTask(httpclient, queue));
|
||||
EXECUTOR.execute(new AsyncTask(httpclient, queue));
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,11 +95,11 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
}
|
||||
|
||||
public Executor getExecutor() {
|
||||
return EXCUTOR;
|
||||
return EXECUTOR;
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.ThreadPoolCreationRule")
|
||||
private static final Executor EXCUTOR = Executors.newScheduledThreadPool(100, new NotifyThreadFactory());
|
||||
private static final Executor EXECUTOR = Executors.newScheduledThreadPool(100, new NotifyThreadFactory());
|
||||
|
||||
private RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(PropertyUtil.getNotifyConnectTimeout())
|
||||
@ -105,7 +108,7 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
private CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
|
||||
.setDefaultRequestConfig(requestConfig).build();
|
||||
|
||||
static final Logger log = LoggerFactory.getLogger(AsyncNotifyService.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(AsyncNotifyService.class);
|
||||
|
||||
private ServerListService serverListService;
|
||||
|
||||
@ -118,15 +121,11 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
executeAsyncInvoke();
|
||||
|
||||
}
|
||||
|
||||
private void executeAsyncInvoke() {
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
|
||||
NotifySingleTask task = queue.poll();
|
||||
String targetIp = task.getTargetIP();
|
||||
if (serverListService.getServerList().contains(
|
||||
@ -139,11 +138,7 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
task.getLastModified(),
|
||||
LOCAL_IP, ConfigTraceService.NOTIFY_EVENT_UNHEALTH, 0, task.target);
|
||||
// get delay time and set fail count to the task
|
||||
int delay = getDelayTime(task);
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
queue.add(task);
|
||||
AsyncTask asyncTask = new AsyncTask(httpclient, queue);
|
||||
((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
|
||||
asyncTaskExecute(task);
|
||||
} else {
|
||||
HttpGet request = new HttpGet(task.url);
|
||||
request.setHeader(NotifyService.NOTIFY_HEADER_LAST_MODIFIED,
|
||||
@ -152,7 +147,7 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
if (task.isBeta) {
|
||||
request.setHeader("isBeta", "true");
|
||||
}
|
||||
httpclient.execute(request, new AyscNotifyCallBack(httpclient, task));
|
||||
httpclient.execute(request, new AsyncNotifyCallBack(httpclient, task));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,12 +158,20 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
|
||||
}
|
||||
|
||||
class AyscNotifyCallBack implements FutureCallback<HttpResponse> {
|
||||
private void asyncTaskExecute(NotifySingleTask task) {
|
||||
int delay = getDelayTime(task);
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
queue.add(task);
|
||||
AsyncTask asyncTask = new AsyncTask(httpclient, queue);
|
||||
((ScheduledThreadPoolExecutor) EXECUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public AyscNotifyCallBack(CloseableHttpAsyncClient httpclient, NotifySingleTask task
|
||||
) {
|
||||
|
||||
class AsyncNotifyCallBack implements FutureCallback<HttpResponse> {
|
||||
|
||||
public AsyncNotifyCallBack(CloseableHttpAsyncClient httpClient, NotifySingleTask task) {
|
||||
this.task = task;
|
||||
this.httpclient = httpclient;
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,31 +186,19 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
ConfigTraceService.NOTIFY_EVENT_OK, delayed,
|
||||
task.target);
|
||||
} else {
|
||||
log.error("[notify-error] {}, {}, to {}, result {}",
|
||||
new Object[] {task.getDataId(), task.getGroup(),
|
||||
task.target,
|
||||
response.getStatusLine().getStatusCode()});
|
||||
log.error("[notify-error] target:{} dataId:{} group:{} ts:{} code:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified(), response.getStatusLine().getStatusCode());
|
||||
ConfigTraceService.logNotifyEvent(task.getDataId(),
|
||||
task.getGroup(), task.getTenant(), null, task.getLastModified(),
|
||||
LOCAL_IP,
|
||||
ConfigTraceService.NOTIFY_EVENT_ERROR, delayed,
|
||||
task.target);
|
||||
|
||||
|
||||
//get delay time and set fail count to the task
|
||||
int delay = getDelayTime(task);
|
||||
asyncTaskExecute(task);
|
||||
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
|
||||
queue.add(task);
|
||||
AsyncTask asyncTask = new AsyncTask(httpclient, queue);
|
||||
|
||||
((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
|
||||
|
||||
LogUtil.notifyLog.error(
|
||||
"[notify-retry] target:{} dataid:{} group:{} ts:{}",
|
||||
new Object[] {task.target, task.getDataId(),
|
||||
task.getGroup(), task.getLastModified()});
|
||||
LogUtil.notifyLog.error("[notify-retry] target:{} dataId:{} group:{} ts:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified());
|
||||
|
||||
MetricsMonitor.getConfigNotifyException().increment();
|
||||
}
|
||||
@ -218,10 +209,8 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
public void failed(Exception ex) {
|
||||
|
||||
long delayed = System.currentTimeMillis() - task.getLastModified();
|
||||
log.error("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", "
|
||||
+ ex.toString());
|
||||
log.debug("[notify-exception] " + task.getDataId() + ", " + task.getGroup() + ", to " + task.target + ", "
|
||||
+ ex.toString(), ex);
|
||||
log.error("[notify-exception] target:{} dataId:{} group:{} ts:{} ex:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified(), ex.toString());
|
||||
ConfigTraceService.logNotifyEvent(task.getDataId(),
|
||||
task.getGroup(), task.getTenant(), null, task.getLastModified(),
|
||||
LOCAL_IP,
|
||||
@ -229,17 +218,9 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
task.target);
|
||||
|
||||
//get delay time and set fail count to the task
|
||||
int delay = getDelayTime(task);
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
|
||||
queue.add(task);
|
||||
AsyncTask asyncTask = new AsyncTask(httpclient, queue);
|
||||
|
||||
((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
|
||||
LogUtil.notifyLog.error(
|
||||
"[notify-retry] target:{} dataid:{} group:{} ts:{}",
|
||||
new Object[] {task.target, task.getDataId(),
|
||||
task.getGroup(), task.getLastModified()});
|
||||
asyncTaskExecute(task);
|
||||
LogUtil.notifyLog.error("[notify-retry] target:{} dataId:{} group:{} ts:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified());
|
||||
|
||||
MetricsMonitor.getConfigNotifyException().increment();
|
||||
}
|
||||
@ -247,30 +228,19 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
@Override
|
||||
public void cancelled() {
|
||||
|
||||
LogUtil.notifyLog.error(
|
||||
"[notify-exception] target:{} dataid:{} group:{} ts:{}",
|
||||
new Object[] {task.target, task.getGroup(),
|
||||
task.getGroup(), task.getLastModified()},
|
||||
"CANCELED");
|
||||
LogUtil.notifyLog.error("[notify-exception] target:{} dataId:{} group:{} ts:{} method:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified(), "CANCELED");
|
||||
|
||||
//get delay time and set fail count to the task
|
||||
int delay = getDelayTime(task);
|
||||
Queue<NotifySingleTask> queue = new LinkedList<NotifySingleTask>();
|
||||
|
||||
queue.add(task);
|
||||
AsyncTask asyncTask = new AsyncTask(httpclient, queue);
|
||||
|
||||
((ScheduledThreadPoolExecutor)EXCUTOR).schedule(asyncTask, delay, TimeUnit.MILLISECONDS);
|
||||
LogUtil.notifyLog.error(
|
||||
"[notify-retry] target:{} dataid:{} group:{} ts:{}",
|
||||
new Object[] {task.target, task.getDataId(),
|
||||
task.getGroup(), task.getLastModified()});
|
||||
asyncTaskExecute(task);
|
||||
LogUtil.notifyLog.error("[notify-retry] target:{} dataId:{} group:{} ts:{}",
|
||||
task.target, task.getDataId(), task.getGroup(), task.getLastModified());
|
||||
|
||||
MetricsMonitor.getConfigNotifyException().increment();
|
||||
}
|
||||
|
||||
private NotifySingleTask task;
|
||||
private CloseableHttpAsyncClient httpclient;
|
||||
private CloseableHttpAsyncClient httpClient;
|
||||
}
|
||||
|
||||
static class NotifySingleTask extends NotifyTask {
|
||||
@ -339,8 +309,7 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r,
|
||||
"com.alibaba.nacos.AsyncNotifyServiceThread");
|
||||
Thread thread = new Thread(r, "com.alibaba.nacos.AsyncNotifyServiceThread");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
}
|
||||
@ -354,15 +323,15 @@ public class AsyncNotifyService extends AbstractEventListener {
|
||||
*/
|
||||
private static int getDelayTime(NotifySingleTask task) {
|
||||
int failCount = task.getFailCount();
|
||||
int delay = MINRETRYINTERVAL + failCount * failCount * INCREASESTEPS;
|
||||
if (failCount <= MAXCOUNT) {
|
||||
int delay = MIN_RETRY_INTERVAL + failCount * failCount * INCREASE_STEPS;
|
||||
if (failCount <= MAX_COUNT) {
|
||||
task.setFailCount(failCount + 1);
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
|
||||
private static int MINRETRYINTERVAL = 500;
|
||||
private static int INCREASESTEPS = 1000;
|
||||
private static int MAXCOUNT = 6;
|
||||
private static int MIN_RETRY_INTERVAL = 500;
|
||||
private static int INCREASE_STEPS = 1000;
|
||||
private static int MAX_COUNT = 6;
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,11 @@
|
||||
*/
|
||||
package com.alibaba.nacos.config.server.utils;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@ -49,4 +54,9 @@ public class JSONUtils {
|
||||
return mapper.readValue(s, typeReference);
|
||||
}
|
||||
|
||||
public static <T> T deserializeObject(InputStream src, TypeReference<?> typeReference)
|
||||
throws IOException {
|
||||
return mapper.readValue(src, typeReference);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @Description: ZipUtils for import and export
|
||||
* @date 2019/5/14 16:59
|
||||
*/
|
||||
public class ZipUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ZipUtils.class);
|
||||
|
||||
|
||||
public static class ZipItem{
|
||||
|
||||
private String itemName;
|
||||
|
||||
private String itemData;
|
||||
|
||||
public ZipItem(String itemName, String itemData) {
|
||||
this.itemName = itemName;
|
||||
this.itemData = itemData;
|
||||
}
|
||||
|
||||
public String getItemName() {
|
||||
return itemName;
|
||||
}
|
||||
|
||||
public void setItemName(String itemName) {
|
||||
this.itemName = itemName;
|
||||
}
|
||||
|
||||
public String getItemData() {
|
||||
return itemData;
|
||||
}
|
||||
|
||||
public void setItemData(String itemData) {
|
||||
this.itemData = itemData;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnZipResult{
|
||||
|
||||
private List<ZipItem> zipItemList;
|
||||
|
||||
private ZipItem metaDataItem;
|
||||
|
||||
|
||||
public UnZipResult(List<ZipItem> zipItemList, ZipItem metaDataItem) {
|
||||
this.zipItemList = zipItemList;
|
||||
this.metaDataItem = metaDataItem;
|
||||
}
|
||||
|
||||
public List<ZipItem> getZipItemList() {
|
||||
return zipItemList;
|
||||
}
|
||||
|
||||
public void setZipItemList(List<ZipItem> zipItemList) {
|
||||
this.zipItemList = zipItemList;
|
||||
}
|
||||
|
||||
public ZipItem getMetaDataItem() {
|
||||
return metaDataItem;
|
||||
}
|
||||
|
||||
public void setMetaDataItem(ZipItem metaDataItem) {
|
||||
this.metaDataItem = metaDataItem;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] zip(List<ZipItem> source){
|
||||
byte[] result = null;
|
||||
try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ZipOutputStream zipOut = new ZipOutputStream(byteOut)){
|
||||
for (ZipItem item : source) {
|
||||
zipOut.putNextEntry(new ZipEntry(item.getItemName()));
|
||||
zipOut.write(item.getItemData().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
zipOut.flush();
|
||||
zipOut.finish();
|
||||
result = byteOut.toByteArray();
|
||||
} catch (IOException e) {
|
||||
log.error("an error occurred while compressing data.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static UnZipResult unzip(byte[] source) {
|
||||
|
||||
List<ZipItem> itemList = new ArrayList<>();
|
||||
ZipItem metaDataItem = null;
|
||||
try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(source))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zipIn.getNextEntry()) != null && !entry.isDirectory()) {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int offset;
|
||||
while ((offset = zipIn.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, offset);
|
||||
}
|
||||
if(".meta.yml".equals(entry.getName())){
|
||||
metaDataItem = new ZipItem(entry.getName(), out.toString("UTF-8"));
|
||||
} else {
|
||||
itemList.add(new ZipItem(entry.getName(), out.toString("UTF-8")));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("unzip error", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("unzip error", e);
|
||||
}
|
||||
return new UnZipResult(itemList, metaDataItem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -258,5 +258,6 @@
|
||||
<appender-ref ref="startLog"/>
|
||||
</logger>
|
||||
|
||||
|
||||
</included>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nacos-console</artifactId>
|
||||
<!--<packaging>war</packaging>-->
|
||||
|
@ -252,6 +252,45 @@ const I18N_CONF = {
|
||||
pleaseEnterTag: 'Enter Tag',
|
||||
application: 'Application',
|
||||
operation: 'Operation',
|
||||
export: 'Export query results',
|
||||
import: 'Import',
|
||||
uploadBtn: 'Upload File',
|
||||
importSucc: 'The import was successful',
|
||||
importAbort: 'Import abort',
|
||||
importSuccBegin: 'The import was successful,with ',
|
||||
importSuccEnd: 'configuration items imported',
|
||||
importFail: 'Import failed',
|
||||
importDataValidationError: 'No legitimate data was read, please check the imported data file.',
|
||||
metadataIllegal: 'The imported metadata file is illegal',
|
||||
namespaceNotExist: 'namespace does not exist',
|
||||
abortImport: 'Abort import',
|
||||
skipImport: 'Skip',
|
||||
overwriteImport: 'Overwrite',
|
||||
importRemind:
|
||||
'File upload will be imported directly into the configuration, please be careful!',
|
||||
samePreparation: 'Same preparation',
|
||||
targetNamespace: 'Target namespace',
|
||||
conflictConfig: 'Conflict-detected configuration items',
|
||||
failureEntries: 'Failure entries',
|
||||
unprocessedEntries: 'Unprocessed entries',
|
||||
skippedEntries: 'skipped entries',
|
||||
exportSelected: 'Export selected configs',
|
||||
clone: 'Clone',
|
||||
exportSelectedAlertTitle: 'Export config',
|
||||
exportSelectedAlertContent: 'please select the configuration to export',
|
||||
cloneSucc: 'The clone was successful',
|
||||
cloneAbort: 'Clone abort',
|
||||
cloneSuccBegin: 'The clone was successful,with ',
|
||||
cloneSuccEnd: 'configuration items cloned',
|
||||
cloneFail: 'Clone failed',
|
||||
getNamespaceFailed: 'get the namespace failed',
|
||||
startCloning: 'Start Clone',
|
||||
cloningConfiguration: 'Clone config',
|
||||
source: 'Source :',
|
||||
configurationNumber: 'Items:',
|
||||
target: 'Target:',
|
||||
selectNamespace: 'Select Namespace',
|
||||
selectedEntry: '| Selected Entry',
|
||||
},
|
||||
NewConfig: {
|
||||
newListingMain: 'Create Configuration',
|
||||
@ -312,9 +351,12 @@ const I18N_CONF = {
|
||||
},
|
||||
ConfigEditor: {
|
||||
official: 'Official',
|
||||
production: 'Production',
|
||||
beta: 'BETA',
|
||||
wrong: 'Error',
|
||||
submitFailed: 'Cannot be empty, submit failed',
|
||||
toedittitle: 'Edit Configuration',
|
||||
newConfigEditor: 'New Config Editor',
|
||||
toedit: 'Edit Configuration',
|
||||
vdchart: 'Illegal characters not allowed',
|
||||
recipientFrom: 'Data ID cannot be empty',
|
||||
@ -330,8 +372,12 @@ const I18N_CONF = {
|
||||
escExit: 'Press F1 to view in full screen',
|
||||
releaseBeta: 'Press Esc to exit ',
|
||||
release: 'Beta Publish',
|
||||
stopPublishBeta: 'Stop Beta',
|
||||
betaPublish: 'Beta Publish:',
|
||||
betaSwitchPrompt: 'Not checked by default.',
|
||||
publish: 'Publish',
|
||||
back: 'Back',
|
||||
codeValErrorPrompt: 'Configuration information may have syntax errors. Are you sure to submit?',
|
||||
},
|
||||
EditorNameSpace: {
|
||||
notice: 'Notice',
|
||||
|
@ -251,6 +251,44 @@ const I18N_CONF = {
|
||||
pleaseEnterTag: '请输入标签',
|
||||
application: '归属应用:',
|
||||
operation: '操作',
|
||||
export: '导出查询结果',
|
||||
import: '导入配置',
|
||||
uploadBtn: '上传文件',
|
||||
importSucc: '导入成功',
|
||||
importAbort: '导入终止',
|
||||
importSuccBegin: '导入成功,导入了',
|
||||
importSuccEnd: '项配制',
|
||||
importFail: '导入失败',
|
||||
importDataValidationError: '未读取到合法数据,请检查导入的数据文件。',
|
||||
metadataIllegal: '导入的元数据文件非法',
|
||||
namespaceNotExist: 'namespace 不存在',
|
||||
abortImport: '终止导入',
|
||||
skipImport: '跳过',
|
||||
overwriteImport: '覆盖',
|
||||
importRemind: '文件上传后将直接导入配置,请务必谨慎操作!',
|
||||
samePreparation: '相同配制',
|
||||
targetNamespace: '目标空间',
|
||||
conflictConfig: '检测到冲突的配置项',
|
||||
failureEntries: '失败的条目',
|
||||
unprocessedEntries: '未处理的条目',
|
||||
skippedEntries: '跳过的条目',
|
||||
exportSelected: '导出选中的配制',
|
||||
clone: '克隆',
|
||||
exportSelectedAlertTitle: '配制导出',
|
||||
exportSelectedAlertContent: '请选择要导出的配制',
|
||||
cloneSucc: '克隆成功',
|
||||
cloneAbort: '克隆终止',
|
||||
cloneSuccBegin: '克隆成功,克隆了',
|
||||
cloneSuccEnd: '项配制',
|
||||
cloneFail: '克隆失败',
|
||||
getNamespaceFailed: '获取命名空间失败',
|
||||
startCloning: '开始克隆',
|
||||
cloningConfiguration: '克隆配制',
|
||||
source: '源空间:',
|
||||
configurationNumber: '配置数量:',
|
||||
target: '目标空间:',
|
||||
selectNamespace: '请选择命名空间',
|
||||
selectedEntry: '| 选中的条目',
|
||||
},
|
||||
NewConfig: {
|
||||
newListingMain: '新建配置',
|
||||
@ -311,10 +349,13 @@ const I18N_CONF = {
|
||||
},
|
||||
ConfigEditor: {
|
||||
official: '正式',
|
||||
production: '正式',
|
||||
beta: 'BETA',
|
||||
wrong: '错误',
|
||||
submitFailed: '不能为空, 提交失败',
|
||||
toedittitle: '编辑配置',
|
||||
toedit: '编辑配置',
|
||||
newConfigEditor: '新建配置',
|
||||
vdchart: '请勿输入非法字符',
|
||||
recipientFrom: 'Data ID不能为空',
|
||||
homeApplication: 'Group不能为空',
|
||||
@ -322,15 +363,19 @@ const I18N_CONF = {
|
||||
groupNotEmpty: '更多高级选项',
|
||||
tags: '标签:',
|
||||
pleaseEnterTag: '请输入标签',
|
||||
targetEnvironment: '归属应用:',
|
||||
targetEnvironment: '归属应用:',
|
||||
description: '描述:',
|
||||
format: '配置格式:',
|
||||
format: '配置格式:',
|
||||
configcontent: '配置内容',
|
||||
escExit: '按F1显示全屏',
|
||||
releaseBeta: '按Esc退出全屏',
|
||||
release: '发布Beta',
|
||||
stopPublishBeta: '停止Beta',
|
||||
betaPublish: 'Beta发布:',
|
||||
betaSwitchPrompt: '默认不要勾选。',
|
||||
publish: '发布',
|
||||
back: '返回',
|
||||
codeValErrorPrompt: '配置信息可能有语法错误, 确定提交吗?',
|
||||
},
|
||||
EditorNameSpace: {
|
||||
notice: '提示',
|
||||
|
@ -0,0 +1,572 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getParams } from '../../../globalLib';
|
||||
import request from '../../../utils/request';
|
||||
import validateContent from 'utils/validateContent';
|
||||
import SuccessDialog from '../../../components/SuccessDialog';
|
||||
import DiffEditorDialog from '../../../components/DiffEditorDialog';
|
||||
import './index.scss';
|
||||
import {
|
||||
Balloon,
|
||||
Button,
|
||||
Dialog,
|
||||
Field,
|
||||
Form,
|
||||
Checkbox,
|
||||
Icon,
|
||||
Input,
|
||||
Loading,
|
||||
Radio,
|
||||
Switch,
|
||||
Select,
|
||||
Tab,
|
||||
Message,
|
||||
Grid,
|
||||
ConfigProvider,
|
||||
} from '@alifd/next';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
const LANGUAGE_LIST = [
|
||||
{ value: 'text', label: 'TEXT' },
|
||||
{ value: 'json', label: 'JSON' },
|
||||
{ value: 'xml', label: 'XML' },
|
||||
{ value: 'yaml', label: 'YAML' },
|
||||
{ value: 'html', label: 'HTML' },
|
||||
{ value: 'properties', label: 'Properties' },
|
||||
];
|
||||
|
||||
const TAB_LIST = ['production', 'beta'];
|
||||
|
||||
@ConfigProvider.config
|
||||
class ConfigEditor extends React.Component {
|
||||
static displayName = 'ConfigEditor';
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
history: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
isBeta: false,
|
||||
isNewConfig: true,
|
||||
betaPublishSuccess: false,
|
||||
betaIps: '',
|
||||
tabActiveKey: '',
|
||||
form: {
|
||||
dataId: '', // 配置 ID
|
||||
group: '', // 分组
|
||||
content: '', // 配置内容
|
||||
appName: '', // 应用名
|
||||
desc: '', // 描述
|
||||
config_tags: [],
|
||||
type: 'text', // 配置格式
|
||||
},
|
||||
tagDataSource: [],
|
||||
openAdvancedSettings: false,
|
||||
};
|
||||
this.successDialog = React.createRef();
|
||||
this.diffEditorDialog = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const isNewConfig = !getParams('dataId');
|
||||
const group = getParams('group').trim();
|
||||
this.setState({ isNewConfig }, () => {
|
||||
if (!isNewConfig) {
|
||||
this.changeForm(
|
||||
{
|
||||
dataId: getParams('dataId').trim(),
|
||||
group,
|
||||
},
|
||||
() => this.getConfig()
|
||||
);
|
||||
} else {
|
||||
if (group) {
|
||||
this.setState({ group });
|
||||
}
|
||||
this.initMoacoEditor('text', '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initMoacoEditor(language, value) {
|
||||
const container = document.getElementById('container');
|
||||
container.innerHTML = '';
|
||||
this.monacoEditor = null;
|
||||
const options = {
|
||||
value,
|
||||
language: this.state.configType,
|
||||
codeLens: true,
|
||||
selectOnLineNumbers: true,
|
||||
roundedSelection: false,
|
||||
readOnly: false,
|
||||
lineNumbersMinChars: true,
|
||||
theme: 'vs-dark',
|
||||
wordWrapColumn: 120,
|
||||
folding: false,
|
||||
showFoldingControls: 'always',
|
||||
wordWrap: 'wordWrapColumn',
|
||||
cursorStyle: 'line',
|
||||
automaticLayout: true,
|
||||
};
|
||||
if (!window.monaco) {
|
||||
window.importEditor(() => {
|
||||
this.monacoEditor = window.monaco.editor.create(container, options);
|
||||
});
|
||||
} else {
|
||||
this.monacoEditor = window.monaco.editor.create(container, options);
|
||||
}
|
||||
}
|
||||
|
||||
createDiffCodeMirror(leftCode, rightCode) {
|
||||
const target = this.diffEditorDialog.current.getInstance();
|
||||
target.innerHTML = '';
|
||||
this.diffeditor = window.CodeMirror.MergeView(target, {
|
||||
value: leftCode || '',
|
||||
origLeft: null,
|
||||
orig: rightCode || '',
|
||||
lineNumbers: true,
|
||||
mode: this.mode,
|
||||
theme: 'xq-light',
|
||||
highlightDifferences: true,
|
||||
connect: 'align',
|
||||
collapseIdentical: false,
|
||||
});
|
||||
}
|
||||
|
||||
openDiff(cbName) {
|
||||
this.diffcb = cbName;
|
||||
let leftvalue = this.monacoEditor.getValue();
|
||||
let rightvalue = this.codeVal;
|
||||
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
this.diffEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
|
||||
}
|
||||
|
||||
clickTab(tabActiveKey) {
|
||||
this.setState({ tabActiveKey }, () => this.getConfig(tabActiveKey === 'bata'));
|
||||
}
|
||||
|
||||
getCodeVal() {
|
||||
const { locale = {} } = this.props;
|
||||
const { type, content } = this.state.form;
|
||||
const codeVal = this.monacoEditor ? this.monacoEditor.getValue() : content;
|
||||
if (!codeVal) {
|
||||
Message.error({
|
||||
content: locale.submitFailed,
|
||||
align: 'cc cc',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return codeVal;
|
||||
}
|
||||
|
||||
publish() {
|
||||
const { locale = {} } = this.props;
|
||||
const { type } = this.state.form;
|
||||
if (this.state.isNewConfig) {
|
||||
this.validation();
|
||||
}
|
||||
const content = this.getCodeVal();
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
if (validateContent.validate({ content, type })) {
|
||||
return this._publishConfig();
|
||||
} else {
|
||||
Dialog.confirm({
|
||||
content: locale.codeValErrorPrompt,
|
||||
onOk: () => this._publishConfig(),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_publishConfig(beta = false) {
|
||||
const { locale } = this.props;
|
||||
const { betaIps, isNewConfig } = this.state;
|
||||
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
if (beta) {
|
||||
headers.betaIps = betaIps;
|
||||
}
|
||||
const data = { ...this.state.form, content: this.getCodeVal() };
|
||||
return request({
|
||||
url: 'v1/cs/configs',
|
||||
method: 'post',
|
||||
data,
|
||||
transformRequest: [
|
||||
function(data) {
|
||||
let ret = '';
|
||||
for (let it in data) {
|
||||
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&';
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
],
|
||||
headers,
|
||||
}).then(res => {
|
||||
if (res) {
|
||||
if (isNewConfig) {
|
||||
this.setState({ isNewConfig: false });
|
||||
}
|
||||
this.getConfig();
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
publishBeta() {
|
||||
return this._publishConfig(true).then(res => {
|
||||
if (res) {
|
||||
this.setState(
|
||||
{
|
||||
betaPublishSuccess: true,
|
||||
tabActiveKey: 'beta',
|
||||
},
|
||||
() => this.getConfig(true)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stopBeta() {
|
||||
const { locale } = this.props;
|
||||
const { dataId, group } = this.state.form;
|
||||
return request
|
||||
.delete('v1/cs/configs', {
|
||||
params: {
|
||||
beta: true,
|
||||
dataId,
|
||||
group,
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
if (res.data) {
|
||||
this.setState(
|
||||
{
|
||||
isBeta: false,
|
||||
betaPublishSuccess: false,
|
||||
tabActiveKey: '',
|
||||
},
|
||||
() => this.getConfig()
|
||||
);
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
changeForm(item, cb) {
|
||||
const { form } = this.state;
|
||||
this.setState({ form: { ...form, ...item } }, () => {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
this.changeForm({ config_tags: tags });
|
||||
}
|
||||
|
||||
goBack() {
|
||||
const serverId = getParams('serverId') || '';
|
||||
const tenant = getParams('namespace');
|
||||
const searchGroup = getParams('searchGroup') || '';
|
||||
const searchDataId = getParams('searchDataId') || '';
|
||||
this.props.history.push(
|
||||
`/configurationManagement?serverId=${serverId}&group=${searchGroup}&dataId=${searchDataId}&namespace=${tenant}`
|
||||
);
|
||||
}
|
||||
|
||||
getConfig(beta = false) {
|
||||
const namespace = getParams('namespace');
|
||||
const { dataId, group } = this.state.form;
|
||||
return request
|
||||
.get('v1/cs/configs', {
|
||||
params: {
|
||||
dataId,
|
||||
group,
|
||||
beta,
|
||||
show: 'all',
|
||||
namespaceId: namespace,
|
||||
tenant: namespace,
|
||||
},
|
||||
})
|
||||
.then(res => {
|
||||
const { type, content, configTags } = res;
|
||||
this.changeForm({ ...res, config_tags: configTags ? configTags.split(',') : [] });
|
||||
this.initMoacoEditor(type, content);
|
||||
this.codeVal = content;
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
validation() {
|
||||
const { locale } = this.props;
|
||||
const { form } = this.state;
|
||||
const { dataId, group } = form;
|
||||
if (!dataId) {
|
||||
this.setState({
|
||||
dataIdError: {
|
||||
validateState: 'error',
|
||||
help: locale.recipientFrom,
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!group) {
|
||||
this.setState({
|
||||
groupError: {
|
||||
validateState: 'error',
|
||||
help: locale.homeApplication,
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
loading,
|
||||
betaIps,
|
||||
openAdvancedSettings,
|
||||
isBeta,
|
||||
isNewConfig,
|
||||
betaPublishSuccess,
|
||||
form,
|
||||
tagDataSource,
|
||||
tabActiveKey,
|
||||
dataIdError = {},
|
||||
groupError = {},
|
||||
} = this.state;
|
||||
const { locale = {} } = this.props;
|
||||
|
||||
return (
|
||||
<div className="config-editor">
|
||||
<Loading
|
||||
shape="flower"
|
||||
style={{ position: 'relative', width: '100%' }}
|
||||
visible={loading}
|
||||
tip="Loading..."
|
||||
color="#333"
|
||||
>
|
||||
<h1 className="func-title">
|
||||
<div>{locale.toedit}</div>
|
||||
</h1>
|
||||
{betaPublishSuccess && (
|
||||
<Tab shape="wrapped" activeKey={tabActiveKey} onChange={key => this.clickTab(key)}>
|
||||
{TAB_LIST.map(key => (
|
||||
<Tab.Item title={locale[key]} key={key}>
|
||||
{locale[key]}
|
||||
</Tab.Item>
|
||||
))}
|
||||
</Tab>
|
||||
)}
|
||||
<Form className="form">
|
||||
<Form.Item label="Data ID:" required {...dataIdError}>
|
||||
<Input
|
||||
value={form.dataId}
|
||||
onChange={dataId =>
|
||||
this.changeForm({ dataId }, () => this.setState({ dataIdError: {} }))
|
||||
}
|
||||
disabled={!isNewConfig}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Group:" required {...groupError}>
|
||||
<Input
|
||||
value={form.group}
|
||||
onChange={group =>
|
||||
this.changeForm({ group }, () => this.setState({ groupError: {} }))
|
||||
}
|
||||
disabled={!isNewConfig}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label=" ">
|
||||
<div
|
||||
className="switch"
|
||||
onClick={() => this.setState({ openAdvancedSettings: !openAdvancedSettings })}
|
||||
>
|
||||
{openAdvancedSettings ? locale.collapse : locale.groupNotEmpty}
|
||||
</div>
|
||||
</Form.Item>
|
||||
{openAdvancedSettings && (
|
||||
<>
|
||||
<Form.Item label={locale.tags}>
|
||||
<Select
|
||||
size="medium"
|
||||
hasArrow
|
||||
autoWidth
|
||||
mode="tag"
|
||||
filterLocal
|
||||
value={form.config_tags}
|
||||
dataSource={tagDataSource}
|
||||
onChange={config_tags => this.setConfigTags(config_tags)}
|
||||
hasClear
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={locale.targetEnvironment}>
|
||||
<Input value={form.appName} onChange={appName => this.changeForm({ appName })} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item label={locale.description}>
|
||||
<Input.TextArea
|
||||
value={form.desc}
|
||||
aria-label="TextArea"
|
||||
onChange={desc => this.changeForm({ desc })}
|
||||
/>
|
||||
</Form.Item>
|
||||
{!isNewConfig && tabActiveKey !== 'production' && (
|
||||
<Form.Item label={locale.betaPublish}>
|
||||
{!betaPublishSuccess && (
|
||||
<Checkbox checked={isBeta} onChange={isBeta => this.setState({ isBeta })}>
|
||||
{locale.betaSwitchPrompt}
|
||||
</Checkbox>
|
||||
)}
|
||||
{isBeta && (
|
||||
<Input.TextArea
|
||||
aria-label="TextArea"
|
||||
placeholder="127.0.0.1,127.0.0.2"
|
||||
onChange={betaIps => this.setState({ betaIps })}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label={locale.format}>
|
||||
<Radio.Group
|
||||
value={form.type}
|
||||
onChange={type => {
|
||||
this.initMoacoEditor(type, '');
|
||||
this.changeForm({ type });
|
||||
}}
|
||||
>
|
||||
{LANGUAGE_LIST.map(item => (
|
||||
<Radio value={item.value} key={item.value}>
|
||||
{item.label}
|
||||
</Radio>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<div className="help-label">
|
||||
<span>{locale.configcontent}</span>
|
||||
<Balloon
|
||||
trigger={<Icon type="help" size="small" />}
|
||||
align="t"
|
||||
style={{ marginRight: 5 }}
|
||||
triggerType="hover"
|
||||
>
|
||||
<p>{locale.escExit}</p>
|
||||
<p>{locale.releaseBeta}</p>
|
||||
</Balloon>
|
||||
<span>:</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div style={{ clear: 'both', height: 300 }} id="container" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Row>
|
||||
<Col span="24" className="button-list">
|
||||
{isBeta && betaPublishSuccess && tabActiveKey !== 'production' && (
|
||||
<Button
|
||||
size="large"
|
||||
type="primary"
|
||||
onClick={() =>
|
||||
this.stopBeta().then(() => {
|
||||
this.successDialog.current.getInstance().openDialog({
|
||||
title: <div>{locale.stopPublishBeta}</div>,
|
||||
isok: true,
|
||||
...form,
|
||||
});
|
||||
})
|
||||
}
|
||||
>
|
||||
{locale.stopPublishBeta}
|
||||
</Button>
|
||||
)}
|
||||
{isBeta && tabActiveKey !== 'production' && (
|
||||
<Button
|
||||
size="large"
|
||||
type="primary"
|
||||
disabled={!betaIps}
|
||||
onClick={() => this.openDiff('publishBeta')}
|
||||
>
|
||||
{locale.release}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="large"
|
||||
type="primary"
|
||||
disabled={tabActiveKey === 'production'}
|
||||
onClick={() => this.openDiff('publish')}
|
||||
>
|
||||
{locale.publish}
|
||||
</Button>
|
||||
<Button size="large" type="normal" onClick={() => this.goBack()}>
|
||||
{locale.back}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<DiffEditorDialog
|
||||
ref={this.diffEditorDialog}
|
||||
publishConfig={() => {
|
||||
this[this.diffcb]();
|
||||
let title = locale.toedit;
|
||||
if (isNewConfig) {
|
||||
title = locale.newConfigEditor;
|
||||
}
|
||||
if (this.diffcb === 'publishBeta') {
|
||||
title = locale.betaPublish;
|
||||
}
|
||||
if (this.diffcb === 'publish' && tabActiveKey === 'beta') {
|
||||
title = locale.stopPublishBeta;
|
||||
this.stopBeta();
|
||||
}
|
||||
this.successDialog.current.getInstance().openDialog({
|
||||
title: <div>{title}</div>,
|
||||
isok: true,
|
||||
...form,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<SuccessDialog ref={this.successDialog} />
|
||||
</Loading>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigEditor;
|
@ -11,6 +11,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import ConfigEditor from './ConfigEditor';
|
||||
import ConfigEditor from './NewConfigEditor';
|
||||
|
||||
export default ConfigEditor;
|
||||
|
@ -10,3 +10,64 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.config-editor {
|
||||
padding: 10px;
|
||||
.func-title {
|
||||
overflow: hidden;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
margin-bottom: 9px;
|
||||
font-size: 18px;
|
||||
line-height: 36px;
|
||||
color: #73777a;
|
||||
}
|
||||
.form {
|
||||
display: table;
|
||||
.next-form-item {
|
||||
display: table-row;
|
||||
.next-form-item-label {
|
||||
white-space: nowrap;
|
||||
word-break: keep-all;
|
||||
}
|
||||
.next-form-item-control,
|
||||
.next-select {
|
||||
width: 100%;
|
||||
}
|
||||
.next-form-item-label,
|
||||
.next-form-item-control {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
.next-form-item-control {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.next-checkbox-label {
|
||||
color: #73777a;
|
||||
font-weight: normal;
|
||||
}
|
||||
.next-radio-label {
|
||||
color: #73777a;
|
||||
}
|
||||
.switch {
|
||||
color: #33cde5;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.help-label > * {
|
||||
display: inline-block;
|
||||
}
|
||||
.help-label > i {
|
||||
color: #1dc11d;
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
}
|
||||
.button-list {
|
||||
text-align: right;
|
||||
button {
|
||||
margin-left: 1em;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ import {
|
||||
Pagination,
|
||||
Select,
|
||||
Table,
|
||||
Grid,
|
||||
Upload,
|
||||
Message,
|
||||
} from '@alifd/next';
|
||||
import BatchHandle from 'components/BatchHandle';
|
||||
import RegionGroup from 'components/RegionGroup';
|
||||
@ -41,6 +44,8 @@ import './index.scss';
|
||||
import { LANGUAGE_KEY } from '../../../constants';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
const { Row, Col } = Grid;
|
||||
const configsTableSelected = new Map();
|
||||
|
||||
@ConfigProvider.config
|
||||
class ConfigurationManagement extends React.Component {
|
||||
@ -667,6 +672,375 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
exportData() {
|
||||
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
|
||||
'namespace'
|
||||
)}&appName=${this.appName}&ids=`;
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
exportSelectedData() {
|
||||
const { locale = {} } = this.props;
|
||||
if (configsTableSelected.size === 0) {
|
||||
Dialog.alert({
|
||||
title: locale.exportSelectedAlertTitle,
|
||||
content: locale.exportSelectedAlertContent,
|
||||
});
|
||||
} else {
|
||||
let idsStr = '';
|
||||
configsTableSelected.forEach((value, key, map) => {
|
||||
idsStr = `${idsStr + key},`;
|
||||
});
|
||||
let url = `v1/cs/configs?export=true&group=&tenant=&appName=&ids=${idsStr}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
|
||||
cloneSelectedDataConfirm() {
|
||||
const { locale = {} } = this.props;
|
||||
const self = this;
|
||||
self.field.setValue('sameConfigPolicy', 'ABORT');
|
||||
self.field.setValue('cloneTargetSpace', undefined);
|
||||
if (configsTableSelected.size === 0) {
|
||||
Dialog.alert({
|
||||
title: locale.exportSelectedAlertTitle,
|
||||
content: locale.exportSelectedAlertContent,
|
||||
});
|
||||
return;
|
||||
}
|
||||
request({
|
||||
url: 'v1/console/namespaces?namespaceId=',
|
||||
beforeSend() {
|
||||
self.openLoading();
|
||||
},
|
||||
success(data) {
|
||||
if (!data || data.code !== 200 || !data.data) {
|
||||
Dialog.alert({
|
||||
title: locale.getNamespaceFailed,
|
||||
content: locale.getNamespaceFailed,
|
||||
});
|
||||
}
|
||||
let namespaces = data.data;
|
||||
let namespaceSelectData = [];
|
||||
namespaces.forEach(item => {
|
||||
if (self.state.nownamespace_id !== item.namespace) {
|
||||
let dataItem = {};
|
||||
if (item.namespaceShowName === 'public') {
|
||||
dataItem.label = 'public | public';
|
||||
dataItem.value = 'public';
|
||||
} else {
|
||||
dataItem.label = `${item.namespaceShowName} | ${item.namespace}`;
|
||||
dataItem.value = item.namespace;
|
||||
}
|
||||
namespaceSelectData.push(dataItem);
|
||||
}
|
||||
});
|
||||
const cloneConfirm = Dialog.confirm({
|
||||
title: locale.cloningConfiguration,
|
||||
footer: false,
|
||||
content: (
|
||||
<div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.source}</span>
|
||||
<span style={{ color: '#49D2E7' }}>{self.state.nownamespace_name} </span>|{' '}
|
||||
{self.state.nownamespace_id}
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.configurationNumber}</span>
|
||||
<span style={{ color: '#49D2E7' }}>{configsTableSelected.size} </span>
|
||||
{locale.selectedEntry}
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: 'red', marginRight: 2, marginLeft: -10 }}>{'*'}</span>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.target}</span>
|
||||
<Select
|
||||
style={{ width: 450 }}
|
||||
placeholder={locale.selectNamespace}
|
||||
size={'medium'}
|
||||
hasArrow
|
||||
showSearch
|
||||
hasClear={false}
|
||||
mode="single"
|
||||
dataSource={namespaceSelectData}
|
||||
onChange={(value, actionType, item) => {
|
||||
if (value) {
|
||||
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
|
||||
self.field.setValue('cloneTargetSpace', value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
<span id={'cloneTargetSpaceSelectErr'} style={{ color: 'red', display: 'none' }}>
|
||||
{locale.selectNamespace}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.samePreparation}:</span>
|
||||
<Select
|
||||
style={{ width: 130 }}
|
||||
size={'medium'}
|
||||
hasArrow
|
||||
mode="single"
|
||||
filterLocal={false}
|
||||
defaultValue={'ABORT'}
|
||||
dataSource={[
|
||||
{
|
||||
label: locale.abortImport,
|
||||
value: 'ABORT',
|
||||
},
|
||||
{
|
||||
label: locale.skipImport,
|
||||
value: 'SKIP',
|
||||
},
|
||||
{
|
||||
label: locale.overwriteImport,
|
||||
value: 'OVERWRITE',
|
||||
},
|
||||
]}
|
||||
hasClear={false}
|
||||
onChange={(value, actionType, item) => {
|
||||
if (value) {
|
||||
self.field.setValue('sameConfigPolicy', value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={() => {
|
||||
if (!self.field.getValue('cloneTargetSpace')) {
|
||||
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'inline';
|
||||
return;
|
||||
} else {
|
||||
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
|
||||
}
|
||||
let idsStr = '';
|
||||
configsTableSelected.forEach((value, key, map) => {
|
||||
idsStr = `${idsStr + key},`;
|
||||
});
|
||||
let cloneTargetSpace = self.field.getValue('cloneTargetSpace');
|
||||
let sameConfigPolicy = self.field.getValue('sameConfigPolicy');
|
||||
request({
|
||||
url: `v1/cs/configs?clone=true&tenant=${cloneTargetSpace}&policy=${sameConfigPolicy}&ids=${idsStr}`,
|
||||
beforeSend() {
|
||||
self.openLoading();
|
||||
},
|
||||
success(ret) {
|
||||
self.processImportAndCloneResult(ret, locale, cloneConfirm, false);
|
||||
},
|
||||
error(data) {
|
||||
self.setState({
|
||||
dataSource: [],
|
||||
total: 0,
|
||||
currentPage: 0,
|
||||
});
|
||||
},
|
||||
complete() {
|
||||
self.closeLoading();
|
||||
},
|
||||
});
|
||||
}}
|
||||
data-spm-click={'gostr=/aliyun;locaid=doClone'}
|
||||
>
|
||||
{locale.startCloning}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
},
|
||||
error(data) {
|
||||
self.setState({
|
||||
dataSource: [],
|
||||
total: 0,
|
||||
currentPage: 0,
|
||||
});
|
||||
},
|
||||
complete() {
|
||||
self.closeLoading();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
processImportAndCloneResult(ret, locale, confirm, isImport) {
|
||||
const resultCode = ret.code;
|
||||
if (resultCode === 200) {
|
||||
confirm.hide();
|
||||
if (ret.data.failData && ret.data.failData.length > 0) {
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importAbort : locale.cloneAbort,
|
||||
content: (
|
||||
<div style={{ width: '500px' }}>
|
||||
<h4>
|
||||
{locale.conflictConfig}:{ret.data.failData[0].group}/{ret.data.failData[0].dataId}
|
||||
</h4>
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<h5>
|
||||
{locale.failureEntries}: {ret.data.failData.length}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.failData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unprocessedEntries}: {ret.data.skipData ? ret.data.skipData.length : 0}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} else if (ret.data.skipCount && ret.data.skipCount > 0) {
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importSucc : locale.cloneSucc,
|
||||
content: (
|
||||
<div style={{ width: '500px' }}>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.skippedEntries}: {ret.data.skipData.length}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
let message = `${isImport ? locale.importSuccBegin : locale.cloneSuccBegin}${
|
||||
ret.data.succCount
|
||||
}${isImport ? locale.importSuccEnd : locale.cloneSuccEnd}`;
|
||||
Message.success(message);
|
||||
}
|
||||
this.getData();
|
||||
} else {
|
||||
let alertContent = isImport ? locale.importFailMsg : locale.cloneFailMsg;
|
||||
if (resultCode === 100001) {
|
||||
alertContent = locale.namespaceNotExist;
|
||||
}
|
||||
if (resultCode === 100002) {
|
||||
alertContent = locale.metadataIllegal;
|
||||
}
|
||||
if (resultCode === 100003 || resultCode === 100004 || resultCode === 100005) {
|
||||
alertContent = locale.importDataValidationError;
|
||||
}
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importFail : locale.cloneFail,
|
||||
content: alertContent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
importData() {
|
||||
const { locale = {} } = this.props;
|
||||
const self = this;
|
||||
self.field.setValue('sameConfigPolicy', 'ABORT');
|
||||
const uploadProps = {
|
||||
accept: 'application/zip',
|
||||
action: `v1/cs/configs?import=true&namespace=${getParams('namespace')}`,
|
||||
data: {
|
||||
policy: self.field.getValue('sameConfigPolicy'),
|
||||
},
|
||||
beforeUpload(file, options) {
|
||||
options.data = {
|
||||
policy: self.field.getValue('sameConfigPolicy'),
|
||||
};
|
||||
return options;
|
||||
},
|
||||
onSuccess(ret) {
|
||||
self.processImportAndCloneResult(ret.response, locale, importConfirm, true);
|
||||
},
|
||||
onError(err) {
|
||||
Dialog.alert({
|
||||
title: locale.importFail,
|
||||
content: locale.importDataValidationError,
|
||||
});
|
||||
},
|
||||
};
|
||||
const importConfirm = Dialog.confirm({
|
||||
title: locale.import,
|
||||
footer: false,
|
||||
content: (
|
||||
<div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.targetNamespace}:</span>
|
||||
<span style={{ color: '#49D2E7' }}>{this.state.nownamespace_name} </span>|{' '}
|
||||
{this.state.nownamespace_id}
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ color: '#999', marginRight: 5 }}>{locale.samePreparation}:</span>
|
||||
<Select
|
||||
style={{ width: 130 }}
|
||||
size={'medium'}
|
||||
hasArrow
|
||||
mode="single"
|
||||
filterLocal={false}
|
||||
defaultValue={'ABORT'}
|
||||
dataSource={[
|
||||
{
|
||||
label: locale.abortImport,
|
||||
value: 'ABORT',
|
||||
},
|
||||
{
|
||||
label: locale.skipImport,
|
||||
value: 'SKIP',
|
||||
},
|
||||
{
|
||||
label: locale.overwriteImport,
|
||||
value: 'OVERWRITE',
|
||||
},
|
||||
]}
|
||||
hasClear={false}
|
||||
onChange={function(value, actionType, item) {
|
||||
self.field.setValue('sameConfigPolicy', value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Icon type="prompt" style={{ color: '#FFA003', marginRight: '10px' }} />
|
||||
{locale.importRemind}
|
||||
</div>
|
||||
<div>
|
||||
<Upload
|
||||
name={'file'}
|
||||
listType="text"
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsImport'}
|
||||
{...uploadProps}
|
||||
>
|
||||
<Button type="primary">{locale.uploadBtn}</Button>
|
||||
</Upload>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
configsTableOnSelect(selected, record, records) {
|
||||
if (selected) {
|
||||
configsTableSelected.set(record.id, record);
|
||||
} else {
|
||||
configsTableSelected.delete(record.id);
|
||||
}
|
||||
}
|
||||
|
||||
configsTableOnSelectAll(selected, records) {
|
||||
if (selected) {
|
||||
records.forEach((record, i) => {
|
||||
configsTableSelected.set(record.id, record);
|
||||
});
|
||||
} else {
|
||||
configsTableSelected.clear();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
return (
|
||||
@ -792,6 +1166,26 @@ class ConfigurationManagement extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.exportData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.export}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.importData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.import}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<br />
|
||||
<Form.Item
|
||||
style={this.inApp ? { display: 'none' } : {}}
|
||||
@ -844,6 +1238,10 @@ class ConfigurationManagement extends React.Component {
|
||||
fixedHeader
|
||||
maxBodyHeight={400}
|
||||
ref={'dataTable'}
|
||||
rowSelection={{
|
||||
onSelect: this.configsTableOnSelect,
|
||||
onSelectAll: this.configsTableOnSelectAll,
|
||||
}}
|
||||
>
|
||||
<Table.Column title={'Data Id'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
@ -856,16 +1254,36 @@ class ConfigurationManagement extends React.Component {
|
||||
</Table>
|
||||
{this.state.dataSource.length > 0 && (
|
||||
<div style={{ marginTop: 10, overflow: 'hidden' }}>
|
||||
<Pagination
|
||||
style={{ float: 'right' }}
|
||||
pageSizeList={[10, 20, 30]}
|
||||
pageSizeSelector={'dropdown'}
|
||||
onPageSizeChange={this.handlePageSizeChange.bind(this)}
|
||||
current={this.state.currentPage}
|
||||
total={this.state.total}
|
||||
pageSize={this.state.pageSize}
|
||||
onChange={this.changePage.bind(this)}
|
||||
/>
|
||||
<div style={{ float: 'left' }}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginLeft: 60, marginRight: 10 }}
|
||||
onClick={this.exportSelectedData.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.exportSelected}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.cloneSelectedDataConfirm.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsClone'}
|
||||
>
|
||||
{locale.clone}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ float: 'right' }}>
|
||||
<Pagination
|
||||
style={{ float: 'right' }}
|
||||
pageSizeList={[10, 20, 30]}
|
||||
pageSizeSelector={'dropdown'}
|
||||
onPageSizeChange={this.handlePageSizeChange.bind(this)}
|
||||
current={this.state.currentPage}
|
||||
total={this.state.total}
|
||||
pageSize={this.state.pageSize}
|
||||
onChange={this.changePage.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
8
pom.xml
8
pom.xml
@ -21,7 +21,7 @@
|
||||
<inceptionYear>2018</inceptionYear>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Alibaba NACOS ${project.version}</name>
|
||||
@ -724,6 +724,12 @@
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.keran213539</groupId>
|
||||
<artifactId>commonOkHttp</artifactId>
|
||||
<version>0.4.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -93,6 +93,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.keran213539</groupId>
|
||||
<artifactId>commonOkHttp</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* 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.test.config;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.NacosFactory;
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.api.config.ConfigService;
|
||||
import com.alibaba.nacos.api.config.listener.Listener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.config.server.Config;
|
||||
import com.alibaba.nacos.naming.misc.HttpClient.HttpResult;
|
||||
import com.alibaba.nacos.test.naming.NamingBase;
|
||||
import com.alibaba.nacos.test.naming.Params;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity.HeadersBuilder;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* @author xiaochun.xxc
|
||||
* @date 2019-07-03
|
||||
**/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class ConfigBeta_ITCase {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String url;
|
||||
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
static final String CONFIG_CONTROLLER_PATH = "/nacos/v1/cs";
|
||||
|
||||
String dataId = "com.dungu.test";
|
||||
String group = "default";
|
||||
String tenant = "dungu";
|
||||
String content = "test";
|
||||
String appName = "nacos";
|
||||
|
||||
@Before
|
||||
public void init() throws NacosException {
|
||||
url = String.format("http://localhost:%d", port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 正常发布Beta配置
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void publishBetaConfig() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("betaIps", "127.0.0.1,127.0.0.2");
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 必选content未设置的发布Beta配置
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void publishBetaConfig_no_content() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("betaIps", "127.0.0.1,127.0.0.2");
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertFalse(response.getStatusCode().is2xxSuccessful());
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 可选参数betaIps不存在时,发布Beta配置应该不成功。
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void publishBetaConfig_noBetaIps_beta() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders(); //不存在betaIps
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
System.out.println(response.getBody());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
|
||||
ResponseEntity<String> response1 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response1.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals(null, JSONObject.parseObject(response1.getBody()).getString("data"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 可选参数betaIps不存在时,发布Beta配置应该不成功。
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void publishBetaConfig_noBetaIps() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders(); //不存在betaIps
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
System.out.println(response.getBody());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
|
||||
ResponseEntity<String> response1 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=false",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response1.getStatusCode().is2xxSuccessful());
|
||||
System.out.println(response1.getBody());
|
||||
Assert.assertEquals(content, response1.getBody());
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 正常获取Beta配置
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void getBetaConfig() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("betaIps", "127.0.0.1,127.0.0.2");
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
|
||||
ResponseEntity<String> response1 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response1.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("com.dungu.test", JSONObject.parseObject(response1.getBody()).getJSONObject("data").getString("dataId"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @TCDescription : 正常删除Beta配置
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void deleteBetaConfig() throws Exception {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("betaIps", "127.0.0.1,127.0.0.2");
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
|
||||
ResponseEntity<String> response1 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response1.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("com.dungu.test", JSONObject.parseObject(response1.getBody()).getJSONObject("data").getString("dataId"));
|
||||
|
||||
ResponseEntity<String> response2 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
Assert.assertTrue(response2.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", JSONObject.parseObject(response2.getBody()).getString("data"));
|
||||
|
||||
ResponseEntity<String> response3 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response3.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals(null, JSONObject.parseObject(response3.getBody()).getString("data"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @TCDescription : beta=false时,删除Beta配置
|
||||
* @TestStep :
|
||||
* @ExpectResult :
|
||||
*/
|
||||
@Test
|
||||
public void deleteBetaConfig_delete_beta_false() throws Exception {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("betaIps", "127.0.0.1,127.0.0.2");
|
||||
|
||||
ResponseEntity<String> response = request(CONFIG_CONTROLLER_PATH + "/configs", headers,
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.appendParam("content", content)
|
||||
.appendParam("config_tags", "")
|
||||
.appendParam("appName", appName)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.POST);
|
||||
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", response.getBody());
|
||||
|
||||
ResponseEntity<String> response1 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response1.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("com.dungu.test", JSONObject.parseObject(response1.getBody()).getJSONObject("data").getString("dataId"));
|
||||
|
||||
ResponseEntity<String> response2 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=false",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.DELETE);
|
||||
Assert.assertTrue(response2.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("true", response2.getBody());
|
||||
|
||||
ResponseEntity<String> response3 = request(CONFIG_CONTROLLER_PATH + "/configs?beta=true",
|
||||
Params.newParams()
|
||||
.appendParam("dataId", dataId)
|
||||
.appendParam("group", group)
|
||||
.appendParam("tenant", tenant)
|
||||
.done(),
|
||||
String.class,
|
||||
HttpMethod.GET);
|
||||
Assert.assertTrue(response3.getStatusCode().is2xxSuccessful());
|
||||
Assert.assertEquals("com.dungu.test", JSONObject.parseObject(response3.getBody()).getJSONObject("data").getString("dataId"));
|
||||
}
|
||||
|
||||
<T> ResponseEntity<T> request(String path, MultiValueMap<String, String> params, Class<T> clazz, HttpMethod httpMethod) {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
||||
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.url.toString() + path)
|
||||
.queryParams(params);
|
||||
|
||||
return this.restTemplate.exchange(
|
||||
builder.toUriString(), httpMethod, entity, clazz);
|
||||
}
|
||||
|
||||
<T> ResponseEntity<T> request(String path, HttpHeaders headers, MultiValueMap<String, String> params, Class<T> clazz, HttpMethod httpMethod) {
|
||||
|
||||
HttpEntity<?> entity = new HttpEntity<T>(headers);
|
||||
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(this.url.toString() + path)
|
||||
.queryParams(params);
|
||||
|
||||
return this.restTemplate.exchange(
|
||||
builder.toUriString(), httpMethod, entity, clazz);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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.test.config;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.client.config.http.HttpAgent;
|
||||
import com.alibaba.nacos.client.config.http.MetricsHttpAgent;
|
||||
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
|
||||
import com.alibaba.nacos.client.config.impl.HttpSimpleClient;
|
||||
import com.alibaba.nacos.config.server.Config;
|
||||
import com.alibaba.nacos.config.server.utils.ZipUtils;
|
||||
import com.github.keran213539.commonOkHttp.CommonOkHttpClient;
|
||||
import com.github.keran213539.commonOkHttp.CommonOkHttpClientBuilder;
|
||||
import com.github.keran213539.commonOkHttp.UploadByteFile;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author klw
|
||||
* @date 2019/5/23 15:26
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class ConfigExportAndImportAPI_ITCase {
|
||||
private static final long TIME_OUT = 2000;
|
||||
private static final String CONFIG_CONTROLLER_PATH = "/v1/cs/configs";
|
||||
|
||||
private CommonOkHttpClient httpClient = new CommonOkHttpClientBuilder().build();
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String SERVER_ADDR = null;
|
||||
|
||||
private HttpAgent agent = null;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
SERVER_ADDR = "http://127.0.0.1"+":"+ port + "/nacos";
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"+":"+port);
|
||||
agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
|
||||
agent.start();
|
||||
|
||||
Map<String, String> prarm = new HashMap<>(7);
|
||||
prarm.put("dataId", "testNoAppname1.yml");
|
||||
prarm.put("group", "DEFAULT_GROUP");
|
||||
prarm.put("content", "test: test");
|
||||
prarm.put("desc", "testNoAppname1");
|
||||
prarm.put("type", "yaml");
|
||||
Assert.assertEquals("true", httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH , prarm,null));
|
||||
prarm.put("dataId", "testNoAppname2.txt");
|
||||
prarm.put("group", "TEST1_GROUP");
|
||||
prarm.put("content", "test: test");
|
||||
prarm.put("desc", "testNoAppname2");
|
||||
prarm.put("type", "text");
|
||||
Assert.assertEquals("true", httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH , prarm,null));
|
||||
prarm.put("dataId", "testHasAppname1.properties");
|
||||
prarm.put("group", "DEFAULT_GROUP");
|
||||
prarm.put("content", "test.test1.value=test");
|
||||
prarm.put("desc", "testHasAppname1");
|
||||
prarm.put("type", "properties");
|
||||
prarm.put("appName", "testApp1");
|
||||
Assert.assertEquals("true", httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH , prarm,null));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup(){
|
||||
HttpSimpleClient.HttpResult result;
|
||||
try {
|
||||
List<String> params2 = Arrays.asList("dataId", "testNoAppname1.yml", "group", "DEFAULT_GROUP", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params2, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
|
||||
List<String> params3 = Arrays.asList("dataId", "testNoAppname2.txt", "group", "TEST1_GROUP", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params3, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
|
||||
List<String> params4 = Arrays.asList("dataId", "testHasAppname1.properties", "group", "DEFAULT_GROUP", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params4, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
|
||||
List<String> params5 = Arrays.asList("dataId", "test1.yml", "group", "TEST_IMPORT", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params4, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
|
||||
List<String> params6 = Arrays.asList("dataId", "test2.txt", "group", "TEST_IMPORT", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params4, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
|
||||
List<String> params7 = Arrays.asList("dataId", "test3.properties", "group", "TEST_IMPORT", "beta", "false");
|
||||
result = agent.httpDelete(CONFIG_CONTROLLER_PATH + "/", null, params4, agent.getEncode(), TIME_OUT);
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, result.code);
|
||||
} catch (Exception e) {
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 3*TIME_OUT)
|
||||
public void testExportByIds(){
|
||||
String getDataUrl = "?search=accurate&dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
JSONObject resultObj = JSON.parseObject(queryResult);
|
||||
JSONArray resultConfigs = resultObj.getJSONArray("pageItems");
|
||||
JSONObject config1 = resultConfigs.getJSONObject(0);
|
||||
JSONObject config2 = resultConfigs.getJSONObject(1);
|
||||
String exportByIdsUrl = "?export=true&tenant=&group=&appName=&ids=" + config1.getIntValue("id")
|
||||
+ "," + config2.getIntValue("id");
|
||||
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(2, zipItemList.size());
|
||||
String config1Name = config1.getString("group") + "/" + config1.getString("dataId");
|
||||
String config2Name = config2.getString("group") + "/" + config2.getString("dataId");
|
||||
for(ZipUtils.ZipItem zipItem : zipItemList){
|
||||
if(!(config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName()))){
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 3*TIME_OUT)
|
||||
public void testExportByGroup(){
|
||||
String getDataUrl = "?search=accurate&dataId=&group=DEFAULT_GROUP&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
JSONObject resultObj = JSON.parseObject(queryResult);
|
||||
JSONArray resultConfigs = resultObj.getJSONArray("pageItems");
|
||||
Assert.assertEquals(2, resultConfigs.size());
|
||||
JSONObject config1 = resultConfigs.getJSONObject(0);
|
||||
JSONObject config2 = resultConfigs.getJSONObject(1);
|
||||
String exportByIdsUrl = "?export=true&tenant=&group=DEFAULT_GROUP&appName=&ids=";
|
||||
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(2, zipItemList.size());
|
||||
String config1Name = config1.getString("group") + "/" + config1.getString("dataId");
|
||||
String config2Name = config2.getString("group") + "/" + config2.getString("dataId");
|
||||
for(ZipUtils.ZipItem zipItem : zipItemList){
|
||||
if(!(config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName()))){
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
// verification metadata
|
||||
Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
|
||||
String metaDataName = packageMetaName("DEFAULT_GROUP", "testHasAppname1.properties");
|
||||
String appName = metaData.get(metaDataName);
|
||||
Assert.assertNotNull(appName);
|
||||
Assert.assertEquals("testApp1", appName);
|
||||
}
|
||||
|
||||
@Test(timeout = 3*TIME_OUT)
|
||||
public void testExportByGroupAndApp(){
|
||||
String getDataUrl = "?search=accurate&dataId=&group=DEFAULT_GROUP&appName=testApp1&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
JSONObject resultObj = JSON.parseObject(queryResult);
|
||||
JSONArray resultConfigs = resultObj.getJSONArray("pageItems");
|
||||
Assert.assertEquals(1, resultConfigs.size());
|
||||
JSONObject config1 = resultConfigs.getJSONObject(0);
|
||||
String exportByIdsUrl = "?export=true&tenant=&group=DEFAULT_GROUP&appName=testApp1&ids=";
|
||||
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.getString("group") + "/" + config1.getString("dataId");
|
||||
for(ZipUtils.ZipItem zipItem : zipItemList){
|
||||
if(!config1Name.equals(zipItem.getItemName())){
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
// verification metadata
|
||||
Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
|
||||
String metaDataName = packageMetaName("DEFAULT_GROUP", "testHasAppname1.properties");
|
||||
String appName = metaData.get(metaDataName);
|
||||
Assert.assertNotNull(appName);
|
||||
Assert.assertEquals("testApp1", appName);
|
||||
}
|
||||
|
||||
@Test(timeout = 3*TIME_OUT)
|
||||
public void testExportAll(){
|
||||
String exportByIdsUrl = "?export=true&tenant=&group=&appName=&ids=";
|
||||
byte[] zipData = httpClient.download(SERVER_ADDR + CONFIG_CONTROLLER_PATH + exportByIdsUrl, null);
|
||||
ZipUtils.UnZipResult unZiped = ZipUtils.unzip(zipData);
|
||||
List<ZipUtils.ZipItem> zipItemList = unZiped.getZipItemList();
|
||||
String config1Name = "DEFAULT_GROUP/testNoAppname1.yml";
|
||||
String config2Name = "TEST1_GROUP/testNoAppname2.txt";
|
||||
String config3Name = "DEFAULT_GROUP/testHasAppname1.properties";
|
||||
int successCount = 0;
|
||||
for(ZipUtils.ZipItem zipItem : zipItemList){
|
||||
if(config1Name.equals(zipItem.getItemName()) || config2Name.equals(zipItem.getItemName()) ||
|
||||
config3Name.equals(zipItem.getItemName())){
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(3, successCount);
|
||||
// verification metadata
|
||||
Map<String, String> metaData = processMetaData(unZiped.getMetaDataItem());
|
||||
String metaDataName = packageMetaName("DEFAULT_GROUP", "testHasAppname1.properties");
|
||||
String appName = metaData.get(metaDataName);
|
||||
Assert.assertNotNull(appName);
|
||||
Assert.assertEquals("testApp1", appName);
|
||||
}
|
||||
|
||||
@Test(timeout = 3*TIME_OUT)
|
||||
public void testImport(){
|
||||
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>(3);
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test1.yml", "test: test1"));
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test2.txt", "test: test1"));
|
||||
zipItemList.add(new ZipUtils.ZipItem("TEST_IMPORT/test3.properties", "test.test1.value=test"));
|
||||
String metaDataStr = "TEST_IMPORT.test2~txt.app=testApp1\r\nTEST_IMPORT.test3~properties.app=testApp2";
|
||||
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", 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");
|
||||
httpClient.post(SERVER_ADDR + CONFIG_CONTROLLER_PATH + importUrl, importPrarm, Collections.singletonList(uploadByteFile), null);
|
||||
String getDataUrl = "?search=accurate&dataId=&group=TEST_IMPORT&appName=&config_tags=&pageNo=1&pageSize=10&tenant=&namespaceId=";
|
||||
String queryResult = httpClient.get(SERVER_ADDR + CONFIG_CONTROLLER_PATH + getDataUrl, null);
|
||||
JSONObject resultObj = JSON.parseObject(queryResult);
|
||||
JSONArray resultConfigs = resultObj.getJSONArray("pageItems");
|
||||
Assert.assertEquals(3, resultConfigs.size());
|
||||
for(int i = 0; i < resultConfigs.size(); i++){
|
||||
JSONObject config = resultConfigs.getJSONObject(i);
|
||||
if(!"TEST_IMPORT".equals(config.getString("group"))){
|
||||
Assert.fail();
|
||||
}
|
||||
switch (config.getString("dataId")){
|
||||
case "test1.yml":
|
||||
case "test2.txt":
|
||||
case "test3.properties":
|
||||
break;
|
||||
default:
|
||||
Assert.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> processMetaData(ZipUtils.ZipItem metaDataZipItem){
|
||||
Map<String, String> metaDataMap = new HashMap<>(16);
|
||||
if(metaDataZipItem != null){
|
||||
String metaDataStr = metaDataZipItem.getItemData();
|
||||
String[] metaDataArr = metaDataStr.split("\r\n");
|
||||
for(String metaDataItem : metaDataArr){
|
||||
String[] metaDataItemArr = metaDataItem.split("=");
|
||||
Assert.assertEquals(2, metaDataItemArr.length);
|
||||
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
|
||||
}
|
||||
}
|
||||
return metaDataMap;
|
||||
}
|
||||
|
||||
private String packageMetaName(String group, String dataId){
|
||||
String tempDataId = dataId;
|
||||
if(tempDataId.contains(".")){
|
||||
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf("."))
|
||||
+ "~" + tempDataId.substring(tempDataId.lastIndexOf(".") + 1);
|
||||
}
|
||||
return group + "." + tempDataId + ".app";
|
||||
}
|
||||
}
|
@ -102,6 +102,20 @@ public class NamingMaintainService_ITCase {
|
||||
System.out.println(instances.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateInstanceWithDisable() throws NacosException {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("netType", "external-update");
|
||||
map.put("version", "2.0");
|
||||
namingService.registerInstance(serviceName, instance);
|
||||
instance.setMetadata(map);
|
||||
instance.setEnabled(false);
|
||||
namingMaintainService.updateInstance(serviceName, instance);
|
||||
List<Instance> instances = namingService.getAllInstances(serviceName, true);
|
||||
|
||||
Assert.assertEquals(instances.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndUpdateService() throws NacosException {
|
||||
|
||||
|
@ -120,7 +120,7 @@ public class RegisterInstance_ITCase {
|
||||
|
||||
String serviceName = randomDomainName();
|
||||
|
||||
System.err.println(serviceName);
|
||||
System.out.println(serviceName);
|
||||
|
||||
naming.registerInstance(serviceName, TEST_IP_4_DOM_1, TEST_PORT, TEST_NEW_CLUSTER_4_DOM_1);
|
||||
|
||||
@ -128,7 +128,7 @@ public class RegisterInstance_ITCase {
|
||||
|
||||
List<Instance> instances = naming.getAllInstances(serviceName);
|
||||
|
||||
Assert.assertEquals(instances.size(), 1);
|
||||
Assert.assertEquals(1, instances.size());
|
||||
Assert.assertTrue(instances.get(0).getInstanceId().contains(serviceName));
|
||||
//Assert.assertEquals(instances2.get(0).getService().getName(), serviceName);
|
||||
Assert.assertEquals(instances.get(0).getIp(), TEST_IP_4_DOM_1);
|
||||
|
Loading…
Reference in New Issue
Block a user