Merge pull request #4610 from KomachiSion/2.0.0-sync-develop

Synchronize changes from develop branch
This commit is contained in:
杨翊 SionYang 2020-12-31 16:32:36 +08:00 committed by GitHub
commit 98a821e98d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1404 additions and 756 deletions

2
.gitignore vendored
View File

@ -15,4 +15,4 @@ derby.log
work
test/logs
derby.log
yarn.lock
yarn.lock

View File

@ -64,7 +64,9 @@ 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";
/**
* Get the key value of some variable value from the system property.
*/

View File

@ -35,7 +35,8 @@ import java.io.Serializable;
@JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = None.class)
@JsonSubTypes({@JsonSubTypes.Type(name = Http.TYPE, value = Http.class),
@JsonSubTypes.Type(name = Mysql.TYPE, value = Mysql.class),
@JsonSubTypes.Type(name = Tcp.TYPE, value = Tcp.class)})
@JsonSubTypes.Type(name = Tcp.TYPE, value = Tcp.class),
@JsonSubTypes.Type(name = None.TYPE, value = None.class)})
public abstract class AbstractHealthChecker implements Cloneable, Serializable {
private static final long serialVersionUID = 3848305577423336421L;

View File

@ -17,6 +17,8 @@
package com.alibaba.nacos.api.naming.utils;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.utils.StringUtils;
/**
@ -71,9 +73,9 @@ public class NamingUtils {
/**
* check combineServiceName format. the serviceName can't be blank.
* <pre>
* serviceName = "@@"; the length = 0; illegal
* serviceName = "group@@"; the length = 1; illegal
* serviceName = "@@serviceName"; the length = 2; legal
* serviceName = "@@"; the length = 0; illegal
* serviceName = "group@@"; the length = 1; illegal
* serviceName = "@@serviceName"; the length = 2; legal
* serviceName = "group@@serviceName"; the length = 2; legal
* </pre>
*
@ -89,7 +91,8 @@ public class NamingUtils {
/**
* Returns a combined string with serviceName and groupName. Such as 'groupName@@serviceName'
* <p>This method works similar with {@link com.alibaba.nacos.api.naming.utils.NamingUtils#getGroupedName} But not verify any parameters.
* <p>This method works similar with {@link com.alibaba.nacos.api.naming.utils.NamingUtils#getGroupedName} But not
* verify any parameters.
*
* </p> etc:
* <p>serviceName | groupName | result</p>
@ -102,4 +105,23 @@ public class NamingUtils {
public static String getGroupedNameOptional(final String serviceName, final String groupName) {
return groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
}
/**
* <p>Check instance param about keep alive.</p>
*
* <pre>
* heart beat timeout must > heart beat interval
* ip delete timeout must > heart beat interval
* </pre>
*
* @param instance need checked instance
* @throws NacosException if check failed, throw exception
*/
public static void checkInstanceIsLegal(Instance instance) throws NacosException {
if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval()
|| instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) {
throw new NacosException(NacosException.INVALID_PARAM,
"Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.");
}
}
}

View File

@ -17,12 +17,18 @@
package com.alibaba.nacos.api.naming.pojo.healthcheck;
import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class HealthCheckerFactoryTest {
@BeforeClass
public static void beforeClass() {
HealthCheckerFactory.registerSubType(TestChecker.class, TestChecker.TYPE);
}
@Test
public void testSerialize() {
@ -33,7 +39,6 @@ public class HealthCheckerFactoryTest {
@Test
public void testSerializeExtend() {
HealthCheckerFactory.registerSubType(TestChecker.class, TestChecker.TYPE);
TestChecker testChecker = new TestChecker();
String actual = HealthCheckerFactory.serialize(testChecker);
assertTrue(actual.contains("\"type\":\"TEST\""));

View File

@ -0,0 +1,70 @@
/*
* 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.logging;
import com.alibaba.nacos.client.logging.log4j2.Log4J2NacosLogging;
import com.alibaba.nacos.client.logging.logback.LogbackNacosLogging;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
/**
* nacos logging.
*
* @author mai.jh
*/
public class NacosLogging {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosLogging.class);
private AbstractNacosLogging nacosLogging;
private boolean isLogback = false;
private NacosLogging() {
try {
Class.forName("ch.qos.logback.classic.Logger");
nacosLogging = new LogbackNacosLogging();
isLogback = true;
} catch (ClassNotFoundException e) {
nacosLogging = new Log4J2NacosLogging();
}
}
private static class NacosLoggingInstance {
private static final NacosLogging INSTANCE = new NacosLogging();
}
public static NacosLogging getInstance() {
return NacosLoggingInstance.INSTANCE;
}
/**
* Load logging Configuration.
*/
public void loadConfiguration() {
try {
nacosLogging.loadConfiguration();
} catch (Throwable t) {
if (isLogback) {
LOGGER.warn("Load Logback Configuration of Nacos fail, message: {}", t.getMessage());
} else {
LOGGER.warn("Load Log4j Configuration of Nacos fail, message: {}", t.getMessage());
}
}
}
}

View File

@ -24,6 +24,7 @@ import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.api.naming.utils.NamingUtils;
import com.alibaba.nacos.api.selector.AbstractSelector;
import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder;
import com.alibaba.nacos.client.naming.core.Balancer;
@ -134,6 +135,7 @@ public class NacosNamingService implements NamingService {
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
clientProxy.registerService(serviceName, groupName, instance);
}

View File

@ -49,6 +49,7 @@ import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.http.client.NacosRestTemplate;
import com.alibaba.nacos.common.http.param.Header;
import com.alibaba.nacos.common.http.param.Query;
import com.alibaba.nacos.common.utils.ConvertUtils;
import com.alibaba.nacos.common.utils.HttpMethod;
import com.alibaba.nacos.common.utils.IPUtil;
import com.alibaba.nacos.common.utils.JacksonUtils;
@ -98,6 +99,8 @@ public class NamingHttpClientProxy implements NamingClientProxy {
private final PushReceiver pushReceiver;
private final int maxRetry;
private int serverPort = DEFAULT_SERVER_PORT;
private ScheduledExecutorService executorService;
@ -115,6 +118,8 @@ public class NamingHttpClientProxy implements NamingClientProxy {
this.initRefreshTask();
this.pushReceiver = new PushReceiver(serviceInfoHolder);
this.serviceInfoHolder = serviceInfoHolder;
this.maxRetry = ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.NAMING_REQUEST_DOMAIN_RETRY_COUNT,
String.valueOf(UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT)));
}
private void initRefreshTask() {
@ -408,6 +413,20 @@ public class NamingHttpClientProxy implements NamingClientProxy {
NacosException exception = new NacosException();
if (serverListManager.isDomain()) {
String nacosDomain = serverListManager.getNacosDomain();
for (int i = 0; i < maxRetry; i++) {
try {
return callServer(api, params, body, nacosDomain, method);
} catch (NacosException e) {
exception = e;
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("request {} failed.", nacosDomain, e);
}
}
}
}
if (servers != null && !servers.isEmpty()) {
Random random = new Random(System.currentTimeMillis());
@ -427,19 +446,6 @@ public class NamingHttpClientProxy implements NamingClientProxy {
}
}
if (serverListManager.isDomain()) {
for (int i = 0; i < UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT; i++) {
try {
return callServer(api, params, body, serverListManager.getNacosDomain(), method);
} catch (NacosException e) {
exception = e;
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("request {} failed.", serverListManager.getNacosDomain(), e);
}
}
}
}
NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(),
exception.getErrMsg());

View File

@ -16,9 +16,7 @@
package com.alibaba.nacos.client.utils;
import com.alibaba.nacos.client.logging.AbstractNacosLogging;
import com.alibaba.nacos.client.logging.log4j2.Log4J2NacosLogging;
import com.alibaba.nacos.client.logging.logback.LogbackNacosLogging;
import com.alibaba.nacos.client.logging.NacosLogging;
import org.slf4j.Logger;
import static org.slf4j.LoggerFactory.getLogger;
@ -34,33 +32,7 @@ public class LogUtils {
public static final Logger NAMING_LOGGER;
static {
try {
boolean isLogback = false;
AbstractNacosLogging nacosLogging;
try {
Class.forName("ch.qos.logback.classic.Logger");
nacosLogging = new LogbackNacosLogging();
isLogback = true;
} catch (ClassNotFoundException e) {
nacosLogging = new Log4J2NacosLogging();
}
try {
nacosLogging.loadConfiguration();
} catch (Throwable t) {
if (isLogback) {
getLogger(LogUtils.class)
.warn("Load Logback Configuration of Nacos fail, message: {}", t.getMessage());
} else {
getLogger(LogUtils.class)
.warn("Load Log4j Configuration of Nacos fail, message: {}", t.getMessage());
}
}
} catch (Throwable ex) {
getLogger(LogUtils.class).warn("Init Nacos Logging fail, message: {}", ex.getMessage());
}
NacosLogging.getInstance().loadConfiguration();
NAMING_LOGGER = getLogger("com.alibaba.nacos.client.naming");
}

View File

@ -27,6 +27,11 @@ import java.io.PrintStream;
*/
public class ExceptionUtil {
/**
* Represents an empty exception, that is, no exception occurs, only a constant.
*/
public static final Exception NONE_EXCEPTION = new RuntimeException("");
public static String getAllExceptionMsg(Throwable e) {
Throwable cause = e;
StringBuilder strBuilder = new StringBuilder();

View File

@ -97,6 +97,7 @@ public class ConfigOpsController {
* @return {@link RestResult}
*/
@GetMapping(value = "/derby")
@Secured(action = ActionTypes.READ, resource = "nacos/admin")
public RestResult<Object> derbyOps(@RequestParam(value = "sql") String sql) {
String selectSign = "select";
String limitSign = "ROWS FETCH NEXT";

View File

@ -47,9 +47,9 @@ public class HistoryController {
* @param group group string value.
* @param tenant tenant string value.
* @param appName appName string value.
* @param pageNo pageNo string value.
* @param pageSize pageSize string value.
* @param modelMap modeMap.
* @param pageNo pageNo integer value.
* @param pageSize pageSize integer value.
* @param modelMap modelMap.
* @return
*/
@GetMapping(params = "search=accurate")

View File

@ -39,8 +39,6 @@ public class ConfigAllInfo extends ConfigInfo {
private String effect;
private String type;
private String schema;
private String configTags;
@ -103,15 +101,7 @@ public class ConfigAllInfo extends ConfigInfo {
public void setEffect(String effect) {
this.effect = effect;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSchema() {
return schema;
}

View File

@ -37,7 +37,7 @@ import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
import com.alibaba.nacos.config.server.utils.ConfigExecutor;
import com.alibaba.nacos.config.server.utils.LogUtil;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.InetUtils;
@ -94,7 +94,7 @@ public class AsyncNotifyService {
Queue<NotifySingleRpcTask> rpcQueue = new LinkedList<NotifySingleRpcTask>();
for (Member member : ipList) {
if (MemberUtils.getSupportedConnectionType(member) == null) {
if (MemberUtil.getSupportedConnectionType(member) == null) {
httpQueue.add(new NotifySingleTask(dataId, group, tenant, tag, dumpTs, member.getAddress(),
evt.isBeta));
} else {

View File

@ -36,12 +36,12 @@ module.exports = Object.assign({}, base, {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true,
parallel: true
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'eval-source-map',
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns:[
@ -54,5 +54,5 @@ module.exports = Object.assign({}, base, {
chunkFilename: '[id].css',
}),
],
mode: 'production',
mode: 'production'
});

View File

@ -176,6 +176,11 @@ const I18N_CONF = {
serviceNameRequired: 'Please enter a service name',
protectThresholdRequired: 'Please enter a protect threshold',
},
InstanceFilter: {
title: 'Metadata Filter',
addFilter: 'Add Filter',
clear: 'Clear',
},
InstanceTable: {
operation: 'Operation',
port: 'Port',
@ -266,8 +271,8 @@ const I18N_CONF = {
configurationManagement8: 'configuration management',
queryResults: 'Search Results: Found',
articleMeetRequirements: 'configuration items',
fuzzyd: 'Enter Data ID',
fuzzyg: 'Enter Group',
fuzzyd: 'Add wildcard \'*\' for fuzzy query',
fuzzyg: 'Add wildcard \'*\' for fuzzy query',
query: 'Search',
advancedQuery9: 'Advanced Query',
application0: 'Application:',

View File

@ -176,6 +176,11 @@ const I18N_CONF = {
serviceNameRequired: '请输入服务名',
protectThresholdRequired: '请输入保护阈值',
},
InstanceFilter: {
title: '元数据过滤',
addFilter: '添加过滤',
clear: '清空',
},
InstanceTable: {
operation: '操作',
port: '端口',
@ -265,8 +270,8 @@ const I18N_CONF = {
configurationManagement8: '配置管理',
queryResults: '查询结果共查询到',
articleMeetRequirements: '条满足要求的配置',
fuzzyd: '模糊查询请输入Data ID',
fuzzyg: '模糊查询请输入Group',
fuzzyd: '添加通配符\'*\'进行模糊查询',
fuzzyg: '添加通配符\'*\'进行模糊查询',
query: '查询',
advancedQuery9: '高级查询',
application0: '归属应用:',

View File

@ -177,9 +177,25 @@ class ConfigurationManagement extends React.Component {
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: res.data,
contentList: showList,
});
}
}
@ -425,6 +441,15 @@ class ConfigurationManagement extends React.Component {
);
}
onChangeSort(dataIndex, order) {
this.dataSource.sort(function(a, b) {
if (order === 'asc') {
return (a[dataIndex] + '').localeCompare(b[dataIndex] + '');
}
return (b[dataIndex] + '').localeCompare(a[dataIndex] + '');
});
}
handlePageSizeChange(pageSize) {
this.setState({ pageSize }, () => this.changePage(1));
}
@ -1363,10 +1388,13 @@ class ConfigurationManagement extends React.Component {
ref="dataTable"
loading={this.state.loading}
rowSelection={this.state.rowSelection}
onSort={this.onChangeSort}
>
<Table.Column title={'Data Id'} dataIndex={'dataId'} />
<Table.Column title={'Group'} dataIndex={'group'} />
{!this.inApp && <Table.Column title={locale.application} dataIndex="appName" />}
<Table.Column sortable={true} title={'Data Id'} dataIndex={'dataId'} />
<Table.Column sortable={true} title={'Group'} dataIndex={'group'} />
{!this.inApp && (
<Table.Column sortable={true} title={locale.application} dataIndex="appName" />
)}
<Table.Column title={locale.operation} cell={this.renderCol.bind(this)} />
</Table>
{configurations.totalCount > 0 && (

View File

@ -63,7 +63,7 @@ class EditClusterDialog extends React.Component {
name,
serviceName,
metadataText,
defaultCheckPort,
defCkport,
useIPPort4Check,
healthChecker,
} = this.state.editCluster;
@ -74,7 +74,7 @@ class EditClusterDialog extends React.Component {
serviceName,
clusterName: name,
metadata: metadataText,
checkPort: defaultCheckPort,
checkPort: defCkport,
useInstancePort4Check: useIPPort4Check,
healthChecker: JSON.stringify(healthChecker),
},
@ -106,7 +106,7 @@ class EditClusterDialog extends React.Component {
const {
healthChecker = {},
useIPPort4Check,
defaultCheckPort = '80',
defCkport = '80',
metadataText = '',
} = editCluster;
const { type, path, headers } = healthChecker;
@ -139,8 +139,8 @@ class EditClusterDialog extends React.Component {
<Form.Item label={`${checkPort}:`}>
<Input
className="in-text"
value={defaultCheckPort}
onChange={defaultCheckPort => this.onChangeCluster({ defaultCheckPort })}
value={defCkport}
onChange={defCkport => this.onChangeCluster({ defCkport })}
/>
</Form.Item>
<Form.Item label={`${useIpPortCheck}:`}>

View File

@ -20,6 +20,7 @@ import { request } from '../../../globalLib';
import { Button, ConfigProvider, Message, Pagination, Table } from '@alifd/next';
import { HEALTHY_COLOR_MAPPING } from './constant';
import EditInstanceDialog from './EditInstanceDialog';
import { isDiff } from './util';
@ConfigProvider.config
class InstanceTable extends React.Component {
@ -30,6 +31,11 @@ class InstanceTable extends React.Component {
clusterName: PropTypes.string,
serviceName: PropTypes.string,
groupName: PropTypes.string,
filters: PropTypes.object,
};
static defaultProps = {
filters: new Map(),
};
constructor(props) {
@ -38,6 +44,7 @@ class InstanceTable extends React.Component {
this.state = {
loading: false,
instance: { count: 0, list: [] },
// tableData: {},
pageNum: 1,
pageSize: 10,
};
@ -56,7 +63,8 @@ class InstanceTable extends React.Component {
}
getInstanceList() {
const { clusterName, serviceName, groupName } = this.props;
const { clusterName, serviceName, groupName, filters } = this.props;
if (!clusterName) return;
const { pageSize, pageNum } = this.state;
request({
@ -118,9 +126,16 @@ class InstanceTable extends React.Component {
const { locale = {} } = this.props;
const { clusterName, serviceName, groupName } = this.props;
const { instance, pageSize, loading } = this.state;
return instance.count ? (
const instanceList = instanceFilter(instance.list, this.props.filters);
const _instance = {
count: instanceList.length,
list: instanceList,
};
return _instance.count ? (
<div>
<Table dataSource={instance.list} loading={loading} getRowProps={this.rowColor}>
<Table dataSource={_instance.list} loading={loading} rowProps={this.rowColor}>
<Table.Column width={138} title="IP" dataIndex="ip" />
<Table.Column width={100} title={locale.port} dataIndex="port" />
<Table.Column
@ -170,10 +185,10 @@ class InstanceTable extends React.Component {
)}
/>
</Table>
{instance.count > pageSize ? (
{_instance.count > pageSize ? (
<Pagination
className="pagination"
total={instance.count}
total={_instance.count}
pageSize={pageSize}
onChange={currentPage => this.onChangePage(currentPage)}
/>
@ -192,4 +207,20 @@ class InstanceTable extends React.Component {
}
}
const instanceFilter = function(array, filters) {
return array.filter(item => {
const { metadata } = item;
let isTargetInstance = true;
filters.forEach((value, key) => {
if (value !== metadata[key]) {
isTargetInstance = false;
return isTargetInstance;
}
});
return isTargetInstance;
});
};
export default InstanceTable;

View File

@ -17,13 +17,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { request } from '@/globalLib';
import { Input, Button, Card, ConfigProvider, Form, Loading, Message } from '@alifd/next';
import { Input, Button, Card, ConfigProvider, Form, Loading, Message, Tag } from '@alifd/next';
import EditServiceDialog from './EditServiceDialog';
import EditClusterDialog from './EditClusterDialog';
import InstanceTable from './InstanceTable';
import { getParameter } from 'utils/nacosutil';
import MonacoEditor from 'components/MonacoEditor';
import { MONACO_READONLY_OPTIONS, METADATA_ENTER } from './constant';
import InstanceFilter from './InstanceFilter';
import './ServiceDetail.scss';
const FormItem = Form.Item;
@ -56,6 +57,7 @@ class ServiceDetail extends React.Component {
service: {},
pageSize: 10,
pageNum: {},
instanceFilters: new Map(),
};
}
@ -94,9 +96,19 @@ class ServiceDetail extends React.Component {
this.editClusterDialog.current.getInstance().show(cluster);
}
setFilters = clusterName => filters => {
const { instanceFilters } = this.state;
const newFilters = new Map(Array.from(instanceFilters));
newFilters.set(clusterName, filters);
this.setState({
instanceFilters: newFilters,
});
};
render() {
const { locale = {} } = this.props;
const { serviceName, groupName, loading, service = {}, clusters } = this.state;
const { serviceName, groupName, loading, service = {}, clusters, instanceFilters } = this.state;
const { metadata = {}, selector = {} } = service;
let metadataText = '';
if (Object.keys(metadata).length) {
@ -175,10 +187,12 @@ class ServiceDetail extends React.Component {
</Button>
}
>
<InstanceFilter setFilters={this.setFilters(cluster.name)} />
<InstanceTable
clusterName={cluster.name}
serviceName={serviceName}
groupName={groupName}
filters={instanceFilters.get(cluster.name)}
/>
</Card>
))}

View File

@ -36,6 +36,9 @@
.cluster-card {
margin-bottom: 30px;
}
.inner-card {
margin-bottom: 10px;
}
}
.service-detail-edit-dialog,

View File

@ -0,0 +1,137 @@
/*
* 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, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Input, ConfigProvider, Button, Form, Tag, Card } from '@alifd/next';
import { isDiff } from './util';
const { Group: TagGroup, Closeable: CloseableTag } = Tag;
const FormItem = Form.Item;
function InstanceFilter(props) {
const [key, setKey] = useState('');
const [value, setValue] = useState('');
const [keyState, setKeyState] = useState('');
const [valueState, setValueState] = useState('');
const [filters, setFilters] = useState(new Map());
const { locale = {} } = props;
const addFilter = () => {
updateInput();
if (key && value) {
const newFilters = new Map(Array.from(filters)).set(key, value);
setFilters(newFilters);
setKeyState('');
setValueState('');
clearInput();
}
};
const removeFilter = key => {
const newFilters = new Map(Array.from(filters));
newFilters.delete(key);
setFilters(newFilters);
};
const clearFilters = () => {
setFilters(new Map());
};
const clearInput = () => {
setKey('');
setValue('');
};
const updateInput = () => {
if (!key) {
setKeyState('error');
} else {
setKeyState('');
}
if (!value) {
setValueState('error');
} else {
setValueState('');
}
};
useEffect(() => {
props.setFilters(filters);
}, [filters]);
return (
<Card contentHeight="auto" className="inner-card">
<Form inline size="small">
<FormItem label={locale.title}>
<FormItem>
<Input
placeholder={'key'}
value={key}
trim
onChange={key => setKey(key)}
onPressEnter={addFilter}
state={keyState}
/>
</FormItem>
<FormItem>
<Input
placeholder={'value'}
value={value}
trim
onChange={value => setValue(value)}
onPressEnter={addFilter}
state={valueState}
/>
</FormItem>
<FormItem label="">
<Button type="primary" onClick={addFilter} style={{ marginRight: 10 }}>
{locale.addFilter}
</Button>
{filters.size > 0 ? (
<Button type="primary" onClick={clearFilters}>
{locale.clear}
</Button>
) : (
''
)}
</FormItem>
</FormItem>
</Form>
<TagGroup>
{Array.from(filters).map(filter => {
return (
<CloseableTag size="medium" key={filter[0]} onClose={() => removeFilter(filter[0])}>
{`${filter[0]} : ${filter[1]}`}
</CloseableTag>
);
})}
</TagGroup>
</Card>
);
}
InstanceFilter.propTypes = {
locale: PropTypes.object,
setFilters: PropTypes.func.isRequired,
};
export default ConfigProvider.config(InstanceFilter);

View File

@ -0,0 +1,26 @@
/*
* 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.
*/
export const isDiff = function(prev, next) {
if (prev.size !== next.size) return true;
let isDiff = false;
next.forEach((value, key) => {
isDiff = value !== prev.get(key);
});
return isDiff;
};

View File

@ -0,0 +1,364 @@
const $ = require('jquery');
const fs = require('fs');
const path = require('path');
const chai = require('chai');
const should = chai.should();
const JWebDriver = require('jwebdriver');
chai.use(JWebDriver.chaiSupportChainPromise);
const resemble = require('resemblejs-node');
resemble.outputSettings({
errorType: 'flatDifferenceIntensity',
});
const rootPath = getRootPath();
module.exports = function() {
let driver, testVars;
before(function() {
let self = this;
driver = self.driver;
testVars = self.testVars;
});
it('× url: http://localhost:8000/#/serviceDetail?name=scenator&groupName=DEFAULT_GROUP', async function() {
await driver.url(
_(`http://localhost:8000/#/serviceDetail?name=scenator&groupName=DEFAULT_GROUP`)
);
});
it('url: http://localhost:8000/#/serviceDetail?name=scenator&groupName=DEFAULT_GROUP', async function() {
await driver.url(
_(`http://localhost:8000/#/serviceDetail?name=scenator&groupName=DEFAULT_GROUP`)
);
});
it('waitBody: ', async function() {
await driver
.sleep(500)
.wait('body', 30000)
.html()
.then(function(code) {
isPageError(code).should.be.false;
});
});
it('click: #username, 142, 43, 0', async function() {
await driver
.sleep(300)
.wait('#username', 30000)
.sleep(300)
.mouseMove(142, 43)
.click(0);
});
it('sendKeys: nacos{ENTER}{TAB}nacos{ENTER}', async function() {
await driver.sendKeys('nacos{ENTER}{TAB}nacos{ENTER}');
});
it('click: 权限控制 ( #root li[role="menuitem"]:nth-child(3) > div[role="listitem"].next-menu-item > div.next-menu-item-inner, 112, 24, 0 )', async function() {
await driver
.sleep(300)
.wait(
'#root li[role="menuitem"]:nth-child(3) > div[role="listitem"].next-menu-item > div.next-menu-item-inner',
30000
)
.sleep(300)
.mouseMove(112, 24)
.click(0);
});
it('click: 服务管理 ( #root li[role="menuitem"]:nth-child(2) > div[role="listitem"].next-menu-item > div.next-menu-item-inner, 83, 27, 0 )', async function() {
await driver
.sleep(300)
.wait(
'#root li[role="menuitem"]:nth-child(2) > div[role="listitem"].next-menu-item > div.next-menu-item-inner',
30000
)
.sleep(300)
.mouseMove(83, 27)
.click(0);
});
it('click: 服务列表 ( #root li[role="menuitem"]:nth-child(1) > div.next-menu-item-inner, 63, 30, 0 )', async function() {
await driver
.sleep(300)
.wait('#root li[role="menuitem"]:nth-child(1) > div.next-menu-item-inner', 30000)
.sleep(300)
.mouseMove(63, 30)
.click(0);
});
it('× click: 详情 ( #root tr.last > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1), 13, 3, 0 )', async function() {
await driver
.sleep(300)
.wait(
'#root tr.last > td[role="gridcell"].last > div.next-table-cell-wrapper > div > a:nth-child(1)',
30000
)
.sleep(300)
.mouseMove(13, 3)
.click(0);
});
it('scrollElementTo: #root div.right-panel, 0, 158', async function() {
await driver
.sleep(300)
.wait('#root div.right-panel', 30000)
.sleep(300)
.scrollElementTo(0, 158);
});
it('× click: div:nth-child(1) > div.next-form-item-control > span.next-small > input, 130, 11, 0', async function() {
await driver
.sleep(300)
.wait('div:nth-child(1) > div.next-form-item-control > span.next-small > input', 30000)
.sleep(300)
.mouseMove(130, 11)
.click(0);
});
it('sendKeys: name{TAB}ins1', async function() {
await driver.sendKeys('name{TAB}ins1');
});
it('× click: 添加过滤 ( //span[text()="添加过滤"], 43, 1, 0 )', async function() {
await driver
.sleep(300)
.wait('//span[text()="添加过滤"]', 30000)
.sleep(300)
.mouseMove(43, 1)
.click(0);
});
it('scrollElementTo: #root div.right-panel, 0, 37', async function() {
await driver
.sleep(300)
.wait('#root div.right-panel', 30000)
.sleep(300)
.scrollElementTo(0, 37);
});
it('scrollTo: 0, 1', async function() {
await driver.scrollTo(0, 1);
});
function _(str) {
if (typeof str === 'string') {
return str.replace(/\{\{(.+?)\}\}/g, function(all, key) {
return testVars[key] || '';
});
} else {
return str;
}
}
};
if (module.parent && /mocha\.js/.test(module.parent.id)) {
runThisSpec();
}
function runThisSpec() {
// read config
let webdriver = process.env['webdriver'] || '';
let proxy = process.env['wdproxy'] || '';
let config = require(rootPath + '/config.json');
let webdriverConfig = Object.assign({}, config.webdriver);
let host = webdriverConfig.host;
let port = webdriverConfig.port || 4444;
let group = webdriverConfig.group || 'default';
let match = webdriver.match(/([^\:]+)(?:\:(\d+))?/);
if (match) {
host = match[1] || host;
port = match[2] || port;
}
let testVars = config.vars;
let browsers = webdriverConfig.browsers;
browsers = browsers.replace(/^\s+|\s+$/g, '');
delete webdriverConfig.host;
delete webdriverConfig.port;
delete webdriverConfig.group;
delete webdriverConfig.browsers;
// read hosts
let hostsPath = rootPath + '/hosts';
let hosts = '';
if (fs.existsSync(hostsPath)) {
hosts = fs.readFileSync(hostsPath).toString();
}
let specName = path
.relative(rootPath, __filename)
.replace(/\\/g, '/')
.replace(/\.js$/, '');
browsers.split(/\s*,\s*/).forEach(function(browserName) {
let caseName = specName + ' : ' + browserName;
let browserInfo = browserName.split(' ');
browserName = browserInfo[0];
let browserVersion = browserInfo[1];
describe(caseName, function() {
this.timeout(600000);
this.slow(1000);
let driver;
before(function() {
let self = this;
let driver = new JWebDriver({
host: host,
port: port,
});
let sessionConfig = Object.assign({}, webdriverConfig, {
group: group,
browserName: browserName,
version: browserVersion,
'ie.ensureCleanSession': true,
});
if (proxy) {
sessionConfig.proxy = {
proxyType: 'manual',
httpProxy: proxy,
sslProxy: proxy,
};
} else if (hosts) {
sessionConfig.hosts = hosts;
}
try {
self.driver = driver
.session(sessionConfig)
.windowSize(1024, 768)
.config({
pageloadTimeout: 30000, // page onload timeout
scriptTimeout: 5000, // sync script timeout
asyncScriptTimeout: 10000, // async script timeout
});
} catch (e) {
console.log(e);
}
self.testVars = testVars;
let casePath = path.dirname(caseName);
if (config.reporter && config.reporter.distDir) {
self.screenshotPath = config.reporter.distDir + '/reports/screenshots/' + casePath;
self.diffbasePath = config.reporter.distDir + '/reports/diffbase/' + casePath;
} else {
self.screenshotPath = rootPath + '/reports/screenshots/' + casePath;
self.diffbasePath = rootPath + '/reports/diffbase/' + casePath;
}
self.caseName = caseName.replace(/.*\//g, '').replace(/\s*[:\.\:\-\s]\s*/g, '_');
mkdirs(self.screenshotPath);
mkdirs(self.diffbasePath);
self.stepId = 0;
return self.driver;
});
module.exports();
beforeEach(function() {
let self = this;
self.stepId++;
if (self.skipAll) {
self.skip();
}
});
afterEach(async function() {
let self = this;
let currentTest = self.currentTest;
let title = currentTest.title;
if (
currentTest.state === 'failed' &&
/^(url|waitBody|switchWindow|switchFrame):/.test(title)
) {
self.skipAll = true;
}
if (
(config.screenshots && config.screenshots.captureAll && !/^(closeWindow):/.test(title)) ||
currentTest.state === 'failed'
) {
const casePath = path.dirname(caseName);
const filepath = `${self.screenshotPath}/${self.caseName}_${self.stepId}`;
const relativeFilePath = `./screenshots/${casePath}/${self.caseName}_${self.stepId}`;
let driver = self.driver;
try {
// catch error when get alert msg
await driver.getScreenshot(filepath + '.png');
let url = await driver.url();
let html = await driver.source();
html = '<!--url: ' + url + ' -->\n' + html;
fs.writeFileSync(filepath + '.html', html);
let cookies = await driver.cookies();
fs.writeFileSync(filepath + '.cookie', JSON.stringify(cookies));
appendToContext(self, relativeFilePath + '.png');
} catch (e) {}
}
});
after(function() {
return this.driver.close();
});
});
});
}
function getRootPath() {
let rootPath = path.resolve(__dirname);
while (rootPath) {
if (fs.existsSync(rootPath + '/config.json')) {
break;
}
rootPath = rootPath.substring(0, rootPath.lastIndexOf(path.sep));
}
return rootPath;
}
function mkdirs(dirname) {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirs(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
}
function callSpec(name) {
try {
require(rootPath + '/' + name)();
} catch (e) {
console.log(e);
process.exit(1);
}
}
function isPageError(code) {
return (
code == '' ||
/ jscontent="errorCode" jstcache="\d+"|diagnoseConnectionAndRefresh|dnserror_unavailable_header|id="reportCertificateErrorRetry"|400 Bad Request|403 Forbidden|404 Not Found|500 Internal Server Error|502 Bad Gateway|503 Service Temporarily Unavailable|504 Gateway Time-out/i.test(
code
)
);
}
function appendToContext(mocha, content) {
try {
const test = mocha.currentTest || mocha.test;
if (!test.context) {
test.context = content;
} else if (Array.isArray(test.context)) {
test.context.push(content);
} else {
test.context = [test.context];
test.context.push(content);
}
} catch (e) {
console.log('error', e);
}
}
function catchError(error) {}

View File

@ -29,7 +29,7 @@ import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.config.server.utils.LogUtil;
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy;
import com.alibaba.nacos.core.remote.Connection;
@ -182,7 +182,7 @@ public class ServerLoaderController {
serverLoaderInfoRequest.setReloadCount(reloadcount);
int count = 0;
for (Member member : serverMemberManager.allMembersWithoutSelf()) {
if (MemberUtils.isSupportedLongCon(member)) {
if (MemberUtil.isSupportedLongCon(member)) {
count++;
completionService.submit(new ServerReLoaderRpcTask(serverLoaderInfoRequest, member));
}
@ -217,7 +217,7 @@ public class ServerLoaderController {
int count = 0;
for (Member member : serverMemberManager.allMembersWithoutSelf()) {
if (MemberUtils.isSupportedLongCon(member)) {
if (MemberUtil.isSupportedLongCon(member)) {
count++;
ServerLoaderInfoRequest serverLoaderInfoRequest = new ServerLoaderInfoRequest();
completionService.submit(new ServerLoaderInfoRpcTask(serverLoaderInfoRequest, member));

View File

@ -24,8 +24,10 @@ import com.alibaba.nacos.auth.common.AuthSystemTypes;
import com.alibaba.nacos.auth.exception.AccessException;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.utils.JacksonUtils;
import com.alibaba.nacos.common.utils.Objects;
import com.alibaba.nacos.config.server.auth.RoleInfo;
import com.alibaba.nacos.config.server.model.User;
import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.console.security.nacos.NacosAuthConfig;
import com.alibaba.nacos.console.security.nacos.NacosAuthManager;
import com.alibaba.nacos.console.security.nacos.roles.NacosRoleServiceImpl;
@ -51,6 +53,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
@ -129,14 +132,21 @@ public class UserController {
*
* @param username username of user
* @param newPassword new password of user
* @param response http response
* @param request http request
* @return ok if update succeed
* @throws IllegalArgumentException if user not exist or oldPassword is incorrect
* @since 1.2.0
*/
@PutMapping
@Secured(resource = NacosAuthConfig.CONSOLE_RESOURCE_NAME_PREFIX + "users", action = ActionTypes.WRITE)
public Object updateUser(@RequestParam String username, @RequestParam String newPassword) {
@Secured(resource = NacosAuthConfig.UPDATE_PASSWORD_ENTRY_POINT, action = ActionTypes.WRITE)
public Object updateUser(@RequestParam String username, @RequestParam String newPassword,
HttpServletResponse response, HttpServletRequest request) throws IOException {
// admin or same user
if (!hasPermission(username, request)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "authorization failed!");
}
User user = userDetailsService.getUserFromDatabase(username);
if (user == null) {
throw new IllegalArgumentException("user " + username + " not exist!");
@ -146,6 +156,23 @@ public class UserController {
return new RestResult<>(200, "update user ok!");
}
private boolean hasPermission(String username, HttpServletRequest request) {
if (!authConfigs.isAuthEnabled()) {
return true;
}
if (Objects.isNull(request.getAttribute(RequestUtil.NACOS_USER_KEY))) {
return false;
}
NacosUser user = (NacosUser) request.getAttribute(RequestUtil.NACOS_USER_KEY);
// admin
if (user.isGlobalAdmin()) {
return true;
}
// same user
return user.getUserName().equals(username);
}
/**
* Get paged users.

View File

@ -56,7 +56,9 @@ public class NacosAuthConfig extends WebSecurityConfigurerAdapter {
public static final String TOKEN_PREFIX = "Bearer ";
public static final String CONSOLE_RESOURCE_NAME_PREFIX = "console/";
public static final String UPDATE_PASSWORD_ENTRY_POINT = CONSOLE_RESOURCE_NAME_PREFIX + "user/password";
@Autowired
private Environment env;

View File

@ -114,7 +114,11 @@ public class NacosRoleServiceImpl {
* @return true if granted, false otherwise
*/
public boolean hasPermission(String username, Permission permission) {
//update password
if (NacosAuthConfig.UPDATE_PASSWORD_ENTRY_POINT.equals(permission.getResource())) {
return true;
}
List<RoleInfo> roleInfoList = getRoles(username);
if (Collections.isEmpty(roleInfoList)) {
return false;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,7 @@ package com.alibaba.nacos.core.cluster;
import com.alibaba.nacos.common.remote.ConnectionType;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.common.utils.IPUtil;
import com.alibaba.nacos.common.utils.Objects;
import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.apache.commons.lang3.StringUtils;
@ -30,7 +31,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Predicate;
@ -41,16 +41,10 @@ import java.util.stream.Collectors;
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class MemberUtils {
private static final String TARGET_MEMBER_CONNECT_REFUSE_ERRMSG = "Connection refused";
private static ServerMemberManager manager;
public static void setManager(ServerMemberManager manager) {
MemberUtils.manager = manager;
}
public class MemberUtil {
protected static final String TARGET_MEMBER_CONNECT_REFUSE_ERRMSG = "Connection refused";
/**
* Information copy.
*
@ -144,17 +138,19 @@ public class MemberUtils {
*
* @param member {@link Member}
*/
public static void onSuccess(Member member) {
Member cloneMember = new Member();
copy(member, cloneMember);
public static void onSuccess(final ServerMemberManager manager, final Member member) {
final NodeState old = member.getState();
manager.getMemberAddressInfos().add(member.getAddress());
cloneMember.setState(NodeState.UP);
cloneMember.setFailAccessCnt(0);
manager.update(cloneMember);
member.setState(NodeState.UP);
member.setFailAccessCnt(0);
if (!Objects.equals(old, member.getState())) {
manager.notifyMemberChange();
}
}
public static void onFail(Member member) {
onFail(member, null);
public static void onFail(final ServerMemberManager manager, final Member member) {
// To avoid null pointer judgments, pass in one NONE_EXCEPTION
onFail(manager, member, ExceptionUtil.NONE_EXCEPTION);
}
/**
@ -163,21 +159,22 @@ public class MemberUtils {
* @param member {@link Member}
* @param ex {@link Throwable}
*/
public static void onFail(Member member, Throwable ex) {
Member cloneMember = new Member();
copy(member, cloneMember);
public static void onFail(final ServerMemberManager manager, final Member member, Throwable ex) {
manager.getMemberAddressInfos().remove(member.getAddress());
cloneMember.setState(NodeState.SUSPICIOUS);
cloneMember.setFailAccessCnt(member.getFailAccessCnt() + 1);
final NodeState old = member.getState();
member.setState(NodeState.SUSPICIOUS);
member.setFailAccessCnt(member.getFailAccessCnt() + 1);
int maxFailAccessCnt = EnvUtil.getProperty("nacos.core.member.fail-access-cnt", Integer.class, 3);
// If the number of consecutive failures to access the target node reaches
// a maximum, or the link request is rejected, the state is directly down
if (cloneMember.getFailAccessCnt() > maxFailAccessCnt || StringUtils
if (member.getFailAccessCnt() > maxFailAccessCnt || StringUtils
.containsIgnoreCase(ex.getMessage(), TARGET_MEMBER_CONNECT_REFUSE_ERRMSG)) {
cloneMember.setState(NodeState.DOWN);
member.setState(NodeState.DOWN);
}
if (!Objects.equals(old, member.getState())) {
manager.notifyMemberChange();
}
manager.update(cloneMember);
}
/**

View File

@ -125,7 +125,6 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
public ServerMemberManager(ServletContext servletContext) throws Exception {
this.serverList = new ConcurrentSkipListMap<>();
EnvUtil.setContextPath(servletContext.getContextPath());
MemberUtils.setManager(this);
init();
}
@ -134,7 +133,7 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
Loggers.CORE.info("Nacos-related cluster resource initialization");
this.port = EnvUtil.getProperty("server.port", Integer.class, 8848);
this.localAddress = InetUtils.getSelfIP() + ":" + port;
this.self = MemberUtils.singleParse(this.localAddress);
this.self = MemberUtil.singleParse(this.localAddress);
this.self.setExtendVal(MemberMetaDataConstants.VERSION, VersionUtils.version);
this.self.setExtendVal(MemberMetaDataConstants.SUPPORT_REMOTE_C_TYPE,
EnvUtil.getProperty(MemberMetaDataConstants.SUPPORT_REMOTE_C_TYPE, ConnectionType.GRPC.getType()));
@ -199,7 +198,7 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
* member information update.
*
* @param newMember {@link Member}
* @return update is success
* @return update is successw
*/
public boolean update(Member newMember) {
Loggers.CLUSTER.debug("member information update : {}", newMember);
@ -213,12 +212,12 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
if (NodeState.DOWN.equals(newMember.getState())) {
memberAddressInfos.remove(newMember.getAddress());
}
boolean isPublishChangeEvent = MemberUtils.isBasicInfoChanged(newMember, member);
boolean isPublishChangeEvent = MemberUtil.isBasicInfoChanged(newMember, member);
newMember.setExtendVal(MemberMetaDataConstants.LAST_REFRESH_TIME, System.currentTimeMillis());
MemberUtils.copy(newMember, member);
MemberUtil.copy(newMember, member);
if (isPublishChangeEvent) {
// member basic data changes and all listeners need to be notified
NotifyCenter.publishEvent(MembersChangeEvent.builder().members(allMembers()).build());
notifyMemberChange();
}
return member;
});
@ -226,6 +225,10 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
return true;
}
void notifyMemberChange() {
NotifyCenter.publishEvent(MembersChangeEvent.builder().members(allMembers()).build());
}
/**
* Whether the node exists within the cluster.
*
@ -311,7 +314,9 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
// Ensure that the node is created only once
tmpMap.put(address, member);
tmpAddressInfo.add(address);
if (NodeState.UP.equals(member.getState())) {
tmpAddressInfo.add(address);
}
}
serverList = tmpMap;
@ -325,10 +330,7 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
// <important> need to put the event publication into a synchronized block to ensure
// that the event publication is sequential
if (hasChange) {
MemberUtils.syncToFile(finalMembers);
Set<Member> healthMembers = MemberUtils.selectTargetMembers(members, member -> {
return !NodeState.DOWN.equals(member.getState());
});
MemberUtil.syncToFile(finalMembers);
Event event = MembersChangeEvent.builder().members(finalMembers).build();
NotifyCenter.publishEvent(event);
}
@ -470,12 +472,12 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
return;
}
if (result.ok()) {
MemberUtils.onSuccess(target);
MemberUtil.onSuccess(ServerMemberManager.this, target);
} else {
Loggers.CLUSTER
.warn("failed to report new info to target node : {}, result : {}",
target.getAddress(), result);
MemberUtils.onFail(target);
MemberUtil.onFail(ServerMemberManager.this, target);
}
}
@ -485,7 +487,7 @@ public class ServerMemberManager implements ApplicationListener<WebServerInitial
.error("failed to report new info to target node : {}, error : {}",
target.getAddress(),
ExceptionUtil.getAllExceptionMsg(throwable));
MemberUtils.onFail(target, throwable);
MemberUtil.onFail(ServerMemberManager.this, target, throwable);
}
@Override

View File

@ -24,7 +24,7 @@ import com.alibaba.nacos.common.http.param.Query;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.core.cluster.AbstractMemberLookup;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.utils.GenericType;
import com.alibaba.nacos.core.utils.GlobalExecutor;
import com.alibaba.nacos.core.utils.Loggers;
@ -147,7 +147,7 @@ public class AddressServerMemberLookup extends AbstractMemberLookup {
isAddressServerHealth = true;
Reader reader = new StringReader(result.getData());
try {
afterLookup(MemberUtils.readServerConf(EnvUtil.analyzeClusterConf(reader)));
afterLookup(MemberUtil.readServerConf(EnvUtil.analyzeClusterConf(reader)));
} catch (Throwable e) {
Loggers.CLUSTER.error("[serverlist] exception for analyzeClusterConf, error : {}",
ExceptionUtil.getAllExceptionMsg(e));

View File

@ -19,7 +19,7 @@ package com.alibaba.nacos.core.cluster.lookup;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.core.cluster.AbstractMemberLookup;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.file.FileChangeEvent;
import com.alibaba.nacos.sys.file.FileWatcher;
@ -74,7 +74,7 @@ public class FileConfigMemberLookup extends AbstractMemberLookup {
Collection<Member> tmpMembers = new ArrayList<>();
try {
List<String> tmp = EnvUtil.readClusterConf();
tmpMembers = MemberUtils.readServerConf(tmp);
tmpMembers = MemberUtil.readServerConf(tmp);
} catch (Throwable e) {
Loggers.CLUSTER
.error("nacos-XXXX [serverlist] failed to get serverlist from disk!, error : {}", e.getMessage());

View File

@ -17,7 +17,7 @@
package com.alibaba.nacos.core.cluster.lookup;
import com.alibaba.nacos.core.cluster.AbstractMemberLookup;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.utils.InetUtils;
@ -34,7 +34,7 @@ public class StandaloneMemberLookup extends AbstractMemberLookup {
public void start() {
if (start.compareAndSet(false, true)) {
String url = InetUtils.getSelfIP() + ":" + EnvUtil.getPort();
afterLookup(MemberUtils.readServerConf(Collections.singletonList(url)));
afterLookup(MemberUtil.readServerConf(Collections.singletonList(url)));
}
}
}

View File

@ -28,7 +28,7 @@ import com.alibaba.nacos.common.remote.client.RpcClientFactory;
import com.alibaba.nacos.common.remote.client.ServerListFactory;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberChangeListener;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.MembersChangeEvent;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.utils.Loggers;
@ -85,7 +85,7 @@ public class ClusterRpcClientProxy extends MemberChangeListener {
//ensure to create client of new members
for (Member member : members) {
ConnectionType supportedConnectionType = MemberUtils.getSupportedConnectionType(member);
ConnectionType supportedConnectionType = MemberUtil.getSupportedConnectionType(member);
if (supportedConnectionType != null) {
createRpcClientAndStart(member, supportedConnectionType);
}
@ -94,7 +94,7 @@ public class ClusterRpcClientProxy extends MemberChangeListener {
//shutdown and remove old members.
Set<Map.Entry<String, RpcClient>> allClientEntrys = RpcClientFactory.getAllClientEntrys();
Iterator<Map.Entry<String, RpcClient>> iterator = allClientEntrys.iterator();
List<String> newMemberKeys = members.stream().filter(a -> MemberUtils.isSupportedLongCon(a))
List<String> newMemberKeys = members.stream().filter(a -> MemberUtil.isSupportedLongCon(a))
.map(a -> memberClientKey(a)).collect(Collectors.toList());
while (iterator.hasNext()) {
Map.Entry<String, RpcClient> next1 = iterator.next();

View File

@ -26,7 +26,7 @@ import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.LoggerUtils;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.NodeState;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.sys.env.EnvUtil;
@ -150,7 +150,7 @@ public class NacosClusterController {
*/
@PostMapping("/server/leave")
public RestResult<String> leave(@RequestBody Collection<String> params) throws Exception {
Collection<Member> memberList = MemberUtils.multiParse(params);
Collection<Member> memberList = MemberUtil.multiParse(params);
memberManager.memberLeave(memberList);
final NacosAsyncRestTemplate nacosAsyncRestTemplate = HttpClientBeanHolder.getNacosAsyncRestTemplate(Loggers.CLUSTER);
final GenericType<RestResult<String>> genericType = new GenericType<RestResult<String>>() {
@ -169,12 +169,12 @@ public class NacosClusterController {
if (result.ok()) {
LoggerUtils.printIfDebugEnabled(Loggers.CLUSTER,
"The node : [{}] success to process the request", member);
MemberUtils.onSuccess(member);
MemberUtil.onSuccess(memberManager, member);
} else {
Loggers.CLUSTER
.warn("The node : [{}] failed to process the request, response is : {}", member,
result);
MemberUtils.onFail(member);
MemberUtil.onFail(memberManager, member);
}
} finally {
latch.countDown();
@ -185,7 +185,7 @@ public class NacosClusterController {
public void onError(Throwable throwable) {
try {
Loggers.CLUSTER.error("Failed to communicate with the node : {}", member);
MemberUtils.onFail(member);
MemberUtil.onFail(memberManager, member);
} finally {
latch.countDown();
}

View File

@ -23,7 +23,7 @@ import com.alibaba.nacos.consistency.cp.CPProtocol;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MemberChangeListener;
import com.alibaba.nacos.core.cluster.MemberMetaDataConstants;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.MembersChangeEvent;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.utils.ClassUtils;
@ -73,7 +73,7 @@ public class ProtocolManager extends MemberChangeListener implements DisposableB
Set<String> nodes = new HashSet<>();
members.forEach(member -> {
final String ip = member.getIp();
final int raftPort = MemberUtils.calculateRaftPort(member);
final int raftPort = MemberUtil.calculateRaftPort(member);
nodes.add(ip + ":" + raftPort);
});
return nodes;

View File

@ -42,7 +42,6 @@ import com.alipay.sofa.jraft.rpc.RaftRpcServerFactory;
import com.alipay.sofa.jraft.rpc.RpcServer;
import com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory;
import com.alipay.sofa.jraft.rpc.impl.MarshallerRegistry;
import com.alipay.sofa.jraft.util.Endpoint;
import com.alipay.sofa.jraft.util.RpcFactoryHelper;
import java.io.File;
@ -77,7 +76,7 @@ public class JRaftUtils {
registry.registerResponseInstance(WriteRequest.class.getName(), WriteRequest.getDefaultInstance());
registry.registerResponseInstance(ReadRequest.class.getName(), ReadRequest.getDefaultInstance());
final RpcServer rpcServer = raftRpcFactory.createRpcServer(new Endpoint(peerId.getIp(), peerId.getPort()));
final RpcServer rpcServer = raftRpcFactory.createRpcServer(peerId.getEndpoint());
RaftRpcServerFactory.addRaftRequestProcessors(rpcServer, RaftExecutor.getRaftCoreExecutor(),
RaftExecutor.getRaftCliServiceExecutor());

View File

@ -23,10 +23,13 @@ import com.alibaba.nacos.common.executor.NameThreadFactory;
import com.alibaba.nacos.common.executor.ThreadPoolManager;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.sys.file.FileChangeEvent;
import com.alibaba.nacos.sys.file.FileWatcher;
import com.alibaba.nacos.sys.file.WatchFileCenter;
import com.alibaba.nacos.sys.utils.ApplicationUtils;
import com.alibaba.nacos.sys.utils.DiskUtils;
import com.alibaba.nacos.sys.utils.InetUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
@ -37,6 +40,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -56,7 +61,9 @@ public class StartingApplicationListener implements NacosApplicationListener {
private static final String LOCAL_IP_PROPERTY_KEY = "nacos.local.ip";
private static final String FIRST_PRE_PROPERTIES = "first_pre";
private static final String NACOS_APPLICATION_CONF = "nacos_application_conf";
private static final Map<String, Object> SOURCES = new ConcurrentHashMap<>();
private ScheduledExecutorService scheduledExecutorService;
@ -69,6 +76,8 @@ public class StartingApplicationListener implements NacosApplicationListener {
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
makeWorkDir();
injectEnvironment(environment);
loadPreProperties(environment);
@ -94,21 +103,19 @@ public class StartingApplicationListener implements NacosApplicationListener {
closeExecutor();
logFilePath();
ApplicationUtils.setStarted(true);
judgeStorageMode(context.getEnvironment());
}
@Override
public void running(ConfigurableApplicationContext context) {
removePreProperties(context.getEnvironment());
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
starting = false;
logFilePath();
makeWorkDir();
LOGGER.error("Startup errors : {}", exception);
ThreadPoolManager.shutdown();
@ -129,13 +136,36 @@ public class StartingApplicationListener implements NacosApplicationListener {
private void loadPreProperties(ConfigurableEnvironment environment) {
try {
environment.getPropertySources().addLast(new OriginTrackedMapPropertySource(FIRST_PRE_PROPERTIES,
EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource())));
} catch (IOException e) {
SOURCES.putAll(EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource()));
environment.getPropertySources()
.addLast(new OriginTrackedMapPropertySource(NACOS_APPLICATION_CONF, SOURCES));
registerWatcher();
} catch (Exception e) {
throw new NacosRuntimeException(NacosException.SERVER_ERROR, e);
}
}
private void registerWatcher() throws NacosException {
WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), new FileWatcher() {
@Override
public void onChange(FileChangeEvent event) {
try {
Map<String, ?> tmp = EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource());
SOURCES.putAll(tmp);
} catch (IOException ignore) {
LOGGER.warn("Failed to monitor file {}", ignore);
}
}
@Override
public boolean interest(String context) {
return StringUtils.contains(context, "application.properties");
}
});
}
private void initSystemProperty() {
if (EnvUtil.getStandaloneMode()) {
System.setProperty(MODE_PROPERTY_KEY_STAND_MODE, "stand alone");
@ -153,10 +183,6 @@ public class StartingApplicationListener implements NacosApplicationListener {
System.setProperty(LOCAL_IP_PROPERTY_KEY, InetUtils.getSelfIP());
}
private void removePreProperties(ConfigurableEnvironment environment) {
environment.getPropertySources().remove(FIRST_PRE_PROPERTIES);
}
private void logClusterConf() {
if (!EnvUtil.getStandaloneMode()) {
try {
@ -174,7 +200,7 @@ public class StartingApplicationListener implements NacosApplicationListener {
}
}
private void logFilePath() {
private void makeWorkDir() {
String[] dirNames = new String[] {"logs", "conf", "data"};
for (String dirName : dirNames) {
LOGGER.info("Nacos Log files: {}", Paths.get(EnvUtil.getNacosHome(), dirName).toString());

View File

@ -0,0 +1,253 @@
/*
* 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.core.cluster;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.common.utils.ThreadUtils;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.web.MockServletContext;
import java.net.ConnectException;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(MockitoJUnitRunner.class)
public class MemberUtilTest {
private static final String IP = "1.1.1.1";
private static final int PORT = 8848;
private ConfigurableEnvironment environment;
private Member originalMember;
private ServerMemberManager memberManager;
@Before
public void setUp() throws Exception {
environment = new MockEnvironment();
EnvUtil.setEnvironment(environment);
final CountDownLatch latch = new CountDownLatch(1);
Subscriber<MembersChangeEvent> subscriber = new MemberChangeListener() {
@Override
public void onEvent(MembersChangeEvent event) {
latch.countDown();
}
};
NotifyCenter.registerSubscriber(subscriber);
memberManager = new ServerMemberManager(new MockServletContext());
latch.await();
NotifyCenter.deregisterSubscriber(subscriber);
originalMember = buildMember();
}
private Member buildMember() {
return Member.builder().ip(IP).port(PORT).state(NodeState.UP).build();
}
@Test
public void testIsBasicInfoChangedNoChangeWithoutExtendInfo() {
Member newMember = buildMember();
assertFalse(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedNoChangeWithExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal("test", "test");
assertFalse(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForIp() {
Member newMember = buildMember();
newMember.setIp("1.1.1.2");
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForPort() {
Member newMember = buildMember();
newMember.setPort(PORT + 1);
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForAddress() {
Member newMember = buildMember();
newMember.setAddress("test");
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForStatus() {
Member newMember = buildMember();
newMember.setState(NodeState.DOWN);
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForMoreBasicExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal(MemberMetaDataConstants.VERSION, "TEST");
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForChangedBasicExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal(MemberMetaDataConstants.WEIGHT, "100");
assertTrue(MemberUtil.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testMemberOnFailWhenReachMaxFailAccessCnt() {
final Member remote = buildMember();
memberManager.memberJoin(Collections.singletonList(remote));
remote.setFailAccessCnt(2);
MemberUtil.onFail(memberManager, remote);
final Member search1 = memberManager.find(remote.getAddress());
Assert.assertEquals(3, search1.getFailAccessCnt());
Assert.assertEquals(NodeState.SUSPICIOUS, search1.getState());
MemberUtil.onFail(memberManager, remote);
final Member search2 = memberManager.find(remote.getAddress());
Assert.assertEquals(4, search2.getFailAccessCnt());
Assert.assertEquals(NodeState.DOWN, search2.getState());
MemberUtil.onSuccess(memberManager, remote);
final Member search3 = memberManager.find(remote.getAddress());
Assert.assertEquals(0, search3.getFailAccessCnt());
Assert.assertEquals(NodeState.UP, search3.getState());
}
@Test
public void testMemberOnFailWhenConnectRefused() {
final Member remote = buildMember();
memberManager.memberJoin(Collections.singletonList(remote));
remote.setFailAccessCnt(1);
MemberUtil.onFail(memberManager, remote, new ConnectException(MemberUtil.TARGET_MEMBER_CONNECT_REFUSE_ERRMSG));
final Member search1 = memberManager.find(remote.getAddress());
Assert.assertEquals(2, search1.getFailAccessCnt());
Assert.assertEquals(NodeState.DOWN, search1.getState());
MemberUtil.onSuccess(memberManager, remote);
final Member search2 = memberManager.find(remote.getAddress());
Assert.assertEquals(0, search2.getFailAccessCnt());
Assert.assertEquals(NodeState.UP, search2.getState());
}
@Test
public void testMemberOnFailListener() throws InterruptedException {
final AtomicBoolean received = new AtomicBoolean(false);
final AtomicReference<MembersChangeEvent> reference = new AtomicReference<>();
NotifyCenter.registerSubscriber(new MemberChangeListener() {
@Override
public void onEvent(MembersChangeEvent event) {
reference.set(event);
received.set(true);
}
});
final Member remote = buildMember();
memberManager.memberJoin(Collections.singletonList(remote));
remote.setFailAccessCnt(1);
MemberUtil.onFail(memberManager, remote, new ConnectException(MemberUtil.TARGET_MEMBER_CONNECT_REFUSE_ERRMSG));
ThreadUtils.sleep(4000);
Assert.assertTrue(received.get());
final MembersChangeEvent event1 = reference.get();
final Member member1 = event1.getMembers().stream().filter(member -> StringUtils.equals(remote.getAddress(), member.getAddress()))
.findFirst().orElseThrow(() -> new AssertionError("member is null"));
Assert.assertEquals(2, member1.getFailAccessCnt());
Assert.assertEquals(NodeState.DOWN, member1.getState());
received.set(false);
MemberUtil.onSuccess(memberManager, remote);
ThreadUtils.sleep(4000);
Assert.assertTrue(received.get());
final MembersChangeEvent event2 = reference.get();
final Member member2 = event2.getMembers().stream().filter(member -> StringUtils.equals(remote.getAddress(), member.getAddress()))
.findFirst().orElseThrow(() -> new AssertionError("member is null"));
Assert.assertEquals(0, member2.getFailAccessCnt());
Assert.assertEquals(NodeState.UP, member2.getState());
}
@Test
public void testMemberOnSuccessWhenMemberAlreadyUP() {
final AtomicBoolean received = new AtomicBoolean(false);
NotifyCenter.registerSubscriber(new MemberChangeListener() {
@Override
public void onEvent(MembersChangeEvent event) {
received.set(true);
}
});
final Member remote = buildMember();
memberManager.updateMember(remote);
MemberUtil.onSuccess(memberManager, remote);
ThreadUtils.sleep(4000);
Assert.assertFalse(received.get());
}
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
@Test
public void testMemberOnFailWhenMemberAlreadyNOUP() {
final AtomicBoolean received = new AtomicBoolean(false);
NotifyCenter.registerSubscriber(new MemberChangeListener() {
@Override
public void onEvent(MembersChangeEvent event) {
received.set(true);
}
});
final Member remote = buildMember();
remote.setState(NodeState.SUSPICIOUS);
memberManager.updateMember(remote);
MemberUtil.onFail(memberManager, remote);
ThreadUtils.sleep(4000);
Assert.assertFalse(received.get());
}
}

View File

@ -1,106 +0,0 @@
/*
* 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.core.cluster;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(MockitoJUnitRunner.class)
public class MemberUtilsTest {
private static final String IP = "1.1.1.1";
private static final int PORT = 8848;
@Mock
private ConfigurableEnvironment environment;
private Member originalMember;
@Before
public void setUp() {
EnvUtil.setEnvironment(environment);
originalMember = buildMember();
}
private Member buildMember() {
return Member.builder().ip(IP).port(PORT).state(NodeState.UP).build();
}
@Test
public void testIsBasicInfoChangedNoChangeWithoutExtendInfo() {
Member newMember = buildMember();
assertFalse(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedNoChangeWithExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal("test", "test");
assertFalse(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForIp() {
Member newMember = buildMember();
newMember.setIp("1.1.1.2");
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForPort() {
Member newMember = buildMember();
newMember.setPort(PORT + 1);
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForAddress() {
Member newMember = buildMember();
newMember.setAddress("test");
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForStatus() {
Member newMember = buildMember();
newMember.setState(NodeState.DOWN);
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForMoreBasicExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal(MemberMetaDataConstants.VERSION, "TEST");
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
@Test
public void testIsBasicInfoChangedForChangedBasicExtendInfo() {
Member newMember = buildMember();
newMember.setExtendVal(MemberMetaDataConstants.WEIGHT, "100");
assertTrue(MemberUtils.isBasicInfoChanged(newMember, originalMember));
}
}

View File

@ -21,8 +21,7 @@ rem added double quotation marks to avoid the issue caused by the folder names c
rem removed the last 5 chars(which means \bin\) to get the base DIR.
set BASE_DIR="%BASE_DIR:~0,-5%"
set DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/,%DEFAULT_SEARCH_LOCATIONS%
set CUSTOM_SEARCH_LOCATIONS=file:%BASE_DIR%/conf/
set MODE="cluster"
set FUNCTION_MODE="all"
@ -84,7 +83,7 @@ set "NACOS_OPTS=%NACOS_OPTS% -Dnacos.home=%BASE_DIR%"
set "NACOS_OPTS=%NACOS_OPTS% -jar %BASE_DIR%\target\%SERVER%.jar"
rem set nacos spring config location
set "NACOS_CONFIG_OPTS=--spring.config.location=%CUSTOM_SEARCH_LOCATIONS%"
set "NACOS_CONFIG_OPTS=--spring.config.additional-location=%CUSTOM_SEARCH_LOCATIONS%"
rem set nacos log4j file location
set "NACOS_LOG4J_OPTS=--logging.config=%BASE_DIR%/conf/nacos-logback.xml"

View File

@ -78,8 +78,7 @@ done
export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export BASE_DIR=`cd $(dirname $0)/..; pwd`
export DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/,${DEFAULT_SEARCH_LOCATIONS}
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/
#===========================================================================================
# JVM Configuration
@ -117,7 +116,7 @@ JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health,${BASE_DIR}/plugi
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -jar ${BASE_DIR}/target/${SERVER}.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.naming.consistency.ephemeral.distro;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder;
import com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder;
import com.alibaba.nacos.naming.consistency.KeyBuilder;
@ -49,15 +50,18 @@ public class DistroHttpRegistry {
private final DistroConsistencyServiceImpl consistencyService;
private final ServerMemberManager memberManager;
public DistroHttpRegistry(DistroComponentHolder componentHolder, DistroTaskEngineHolder taskEngineHolder,
DataStore dataStore, DistroMapper distroMapper, GlobalConfig globalConfig,
DistroConsistencyServiceImpl consistencyService) {
DistroConsistencyServiceImpl consistencyService, ServerMemberManager memberManager) {
this.componentHolder = componentHolder;
this.taskEngineHolder = taskEngineHolder;
this.dataStore = dataStore;
this.distroMapper = distroMapper;
this.globalConfig = globalConfig;
this.consistencyService = consistencyService;
this.memberManager = memberManager;
}
/**
@ -67,7 +71,7 @@ public class DistroHttpRegistry {
public void doRegister() {
componentHolder.registerDataStorage(KeyBuilder.INSTANCE_LIST_KEY_PREFIX,
new DistroDataStorageImpl(dataStore, distroMapper));
componentHolder.registerTransportAgent(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, new DistroHttpAgent());
componentHolder.registerTransportAgent(KeyBuilder.INSTANCE_LIST_KEY_PREFIX, new DistroHttpAgent(memberManager));
componentHolder.registerFailedTaskHandler(KeyBuilder.INSTANCE_LIST_KEY_PREFIX,
new DistroHttpCombinedKeyTaskFailedHandler(globalConfig, taskEngineHolder));
taskEngineHolder.registerNacosTaskProcessor(KeyBuilder.INSTANCE_LIST_KEY_PREFIX,

View File

@ -16,13 +16,14 @@
package com.alibaba.nacos.naming.consistency.ephemeral.distro.component;
import com.alibaba.nacos.naming.consistency.KeyBuilder;
import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.distributed.distro.component.DistroCallback;
import com.alibaba.nacos.core.distributed.distro.component.DistroTransportAgent;
import com.alibaba.nacos.core.distributed.distro.entity.DistroData;
import com.alibaba.nacos.core.distributed.distro.entity.DistroKey;
import com.alibaba.nacos.core.distributed.distro.exception.DistroException;
import com.alibaba.nacos.naming.consistency.KeyBuilder;
import com.alibaba.nacos.naming.consistency.ephemeral.distro.combined.DistroHttpCombinedKey;
import com.alibaba.nacos.naming.misc.NamingProxy;
import java.util.ArrayList;
@ -35,6 +36,12 @@ import java.util.List;
*/
public class DistroHttpAgent implements DistroTransportAgent {
private final ServerMemberManager memberManager;
public DistroHttpAgent(ServerMemberManager memberManager) {
this.memberManager = memberManager;
}
@Override
public boolean supportCallbackTransport() {
return false;
@ -42,6 +49,9 @@ public class DistroHttpAgent implements DistroTransportAgent {
@Override
public boolean syncData(DistroData data, String targetServer) {
if (!memberManager.hasMember(targetServer)) {
return true;
}
byte[] dataContent = data.getContent();
return NamingProxy.syncData(dataContent, data.getDistroKey().getTargetServer());
}
@ -53,6 +63,9 @@ public class DistroHttpAgent implements DistroTransportAgent {
@Override
public boolean syncVerifyData(DistroData verifyData, String targetServer) {
if (!memberManager.hasMember(targetServer)) {
return true;
}
NamingProxy.syncCheckSums(verifyData.getContent(), targetServer);
return true;
}

View File

@ -29,6 +29,7 @@ import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.core.ServiceManager;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.NamingResourceParser;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
@ -59,7 +60,7 @@ public class ClusterController {
* @throws Exception if failed
*/
@PutMapping
@Secured(action = ActionTypes.WRITE)
@Secured(action = ActionTypes.WRITE, parser = NamingResourceParser.class)
public String update(HttpServletRequest request) throws Exception {
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);

View File

@ -107,7 +107,7 @@ public class ServiceController {
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String create(@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,
@RequestParam String serviceName, @RequestParam(required = false) float protectThreshold,
@RequestParam String serviceName, @RequestParam(required = false, defaultValue = "0.0F") float protectThreshold,
@RequestParam(defaultValue = StringUtils.EMPTY) String metadata,
@RequestParam(defaultValue = StringUtils.EMPTY) String selector) throws Exception {
ServiceMetadata serviceMetadata = new ServiceMetadata();

View File

@ -67,23 +67,7 @@ public class CatalogServiceV1Impl implements CatalogService {
ObjectNode detailView = JacksonUtils.createEmptyJsonNode();
detailView.replace("service", serviceObject);
List<com.alibaba.nacos.api.naming.pojo.Cluster> clusters = new ArrayList<>();
for (com.alibaba.nacos.naming.core.Cluster cluster : detailedService.getClusterMap().values()) {
com.alibaba.nacos.api.naming.pojo.Cluster clusterView = new com.alibaba.nacos.api.naming.pojo.Cluster();
clusterView.setName(cluster.getName());
clusterView.setHealthChecker(cluster.getHealthChecker());
clusterView.setMetadata(cluster.getMetadata());
clusterView.setUseIPPort4Check(cluster.isUseIPPort4Check());
clusterView.setDefaultPort(cluster.getDefaultPort());
clusterView.setDefaultCheckPort(cluster.getDefaultCheckPort());
clusterView.setServiceName(cluster.getService().getName());
clusters.add(clusterView);
}
detailView.replace("clusters", JacksonUtils.transferToJsonNode(clusters));
detailView.replace("clusters", JacksonUtils.transferToJsonNode(detailedService.getClusterMap().values()));
return detailView;
}

View File

@ -18,13 +18,13 @@ package com.alibaba.nacos.naming.core;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.core.cluster.MemberChangeListener;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.MembersChangeEvent;
import com.alibaba.nacos.core.cluster.NodeState;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.SwitchDomain;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
@ -66,7 +66,7 @@ public class DistroMapper extends MemberChangeListener {
@PostConstruct
public void init() {
NotifyCenter.registerSubscriber(this);
this.healthyList = MemberUtils.simpleMembers(memberManager.allMembers());
this.healthyList = MemberUtil.simpleMembers(memberManager.allMembers());
}
public boolean responsible(Cluster cluster, Instance instance) {
@ -119,8 +119,8 @@ public class DistroMapper extends MemberChangeListener {
int index = distroHash(responsibleTag) % servers.size();
return servers.get(index);
} catch (Throwable e) {
Loggers.SRV_LOG.warn("[NACOS-DISTRO] distro mapper failed, return localhost: " + EnvUtil
.getLocalAddress(), e);
Loggers.SRV_LOG
.warn("[NACOS-DISTRO] distro mapper failed, return localhost: " + EnvUtil.getLocalAddress(), e);
return EnvUtil.getLocalAddress();
}
}
@ -133,8 +133,8 @@ public class DistroMapper extends MemberChangeListener {
public void onEvent(MembersChangeEvent event) {
// Here, the node list must be sorted to ensure that all nacos-server's
// node list is in the same order
List<String> list = MemberUtils.simpleMembers(MemberUtils
.selectTargetMembers(event.getMembers(), member -> !NodeState.DOWN.equals(member.getState())));
List<String> list = MemberUtil.simpleMembers(MemberUtil.selectTargetMembers(event.getMembers(),
member -> NodeState.UP.equals(member.getState()) || NodeState.SUSPICIOUS.equals(member.getState())));
Collections.sort(list);
Collection<String> old = healthyList;
healthyList = Collections.unmodifiableList(list);

View File

@ -809,7 +809,12 @@ public class ServiceManager implements RecordListener<Service> {
if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
instanceMap.remove(instance.getDatumKey());
} else {
instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
Instance oldInstance = instanceMap.get(instance.getDatumKey());
if (oldInstance != null) {
instance.setInstanceId(oldInstance.getInstanceId());
} else {
instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));
}
instanceMap.put(instance.getDatumKey(), instance);
}
@ -956,7 +961,7 @@ public class ServiceManager implements RecordListener<Service> {
List<Instance> instances = service.allIPs();
for (Instance instance : instances) {
if (IPUtil.containsPort(containedInstance)) {
if (StringUtils.equals(instance.getIp() + IPUtil.IP_PORT_SPLITER + instance.getPort(), containedInstance)) {
if (StringUtils.equals(instance.getIp() + IPUtil.IP_PORT_SPLITER + instance.getPort(), containedInstance)) {
contained = true;
break;
}

View File

@ -258,6 +258,8 @@ public class LabelSelector extends ExpressionSelector implements Selector {
return -1;
}
final String labelConsumer = elements.get(index++).split(CONSUMER_PREFIX)[1];
index = skipEmpty(elements, index);
if (index >= elements.size()) {
return -1;
@ -276,9 +278,8 @@ public class LabelSelector extends ExpressionSelector implements Selector {
return -1;
}
String labelProvider = elements.get(index).split(PROVIDER_PREFIX)[1];
final String labelProvider = elements.get(index).split(PROVIDER_PREFIX)[1];
String labelConsumer = elements.get(index++).split(CONSUMER_PREFIX)[1];
if (!labelConsumer.equals(labelProvider)) {
return -1;
}

View File

@ -154,6 +154,9 @@ public class ServiceUtil {
if (start < 0) {
start = 0;
}
if (start >= result.size()) {
return result;
}
int end = start + pageSize;
if (end > result.size()) {
end = result.size();

View File

@ -0,0 +1,75 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.naming.controllers;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.naming.BaseTest;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class ServiceControllerTest extends BaseTest {
@InjectMocks
private ServiceController serviceController;
private MockMvc mockmvc;
@Before
public void before() {
super.before();
mockmvc = MockMvcBuilders.standaloneSetup(serviceController).build();
}
@Test
public void testList() throws Exception {
List<String> serviceNameList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
serviceNameList.add("DEFAULT_GROUP@@providers:com.alibaba.nacos.controller.test:" + i);
}
Mockito.when(serviceManager.getAllServiceNameList(Constants.DEFAULT_NAMESPACE_ID)).thenReturn(serviceNameList);
mockmvc.perform(MockMvcRequestBuilders.get(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/service" + "/list")
.param("pageNo", "2").param("pageSize", "10")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.doms").isArray())
.andExpect(MockMvcResultMatchers.jsonPath("$.doms").isEmpty())
.andExpect(MockMvcResultMatchers.jsonPath("$.count").value(serviceNameList.size()));
}
}

View File

@ -366,9 +366,10 @@ public class EnvUtil {
}
private static Resource getCustomFileResource() {
String path = getProperty("spring.config.location");
String path = getProperty("spring.config.additional-location");
InputStream inputStream = null;
if (StringUtils.isNotBlank(path) && path.contains(FILE_PREFIX)) {
String[] paths = path.split(",");
String[] paths = path.split(",", -1);
path = paths[paths.length - 1].substring(FILE_PREFIX.length());
return getRelativePathResource(path, "application.properties");
}

View File

@ -38,6 +38,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Deprecated
public class NacosAutoRefreshPropertySourceLoader implements PropertySourceLoader {
private final Map<String, Object> properties = new ConcurrentHashMap<>(16);

View File

@ -89,8 +89,7 @@ public class WatchFileCenter {
*/
public static synchronized boolean registerWatcher(final String paths, FileWatcher watcher) throws NacosException {
checkState();
NOW_WATCH_JOB_CNT++;
if (NOW_WATCH_JOB_CNT > MAX_WATCH_FILE_JOB) {
if (NOW_WATCH_JOB_CNT == MAX_WATCH_FILE_JOB) {
return false;
}
WatchDirJob job = MANAGER.get(paths);
@ -98,6 +97,7 @@ public class WatchFileCenter {
job = new WatchDirJob(paths);
job.start();
MANAGER.put(paths, job);
NOW_WATCH_JOB_CNT++;
}
job.addSubscribe(watcher);
return true;
@ -114,6 +114,7 @@ public class WatchFileCenter {
if (job != null) {
job.shutdown();
MANAGER.remove(path);
NOW_WATCH_JOB_CNT--;
return true;
}
return false;
@ -136,6 +137,7 @@ public class WatchFileCenter {
}
}
MANAGER.clear();
NOW_WATCH_JOB_CNT = 0;
LOGGER.warn("[WatchFileCenter] already closed");
}

View File

@ -265,7 +265,7 @@ public final class DiskUtils {
* @return delete success
*/
public static boolean deleteFile(String path, String fileName) {
File file = openFile(path, fileName);
File file = Paths.get(path, fileName).toFile();
if (file.exists()) {
return file.delete();
}

View File

@ -1,6 +1,3 @@
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
com.alibaba.nacos.sys.env.NacosAutoRefreshPropertySourceLoader
# EnvironmentPostProcessor
org.springframework.boot.env.EnvironmentPostProcessor=\
com.alibaba.nacos.sys.env.NacosDefaultPropertySourceEnvironmentPostProcessor

View File

@ -1,91 +0,0 @@
/*
* 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.sys.env;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.utils.ByteUtils;
import com.alibaba.nacos.common.utils.ThreadUtils;
import com.alibaba.nacos.sys.file.FileChangeEvent;
import com.alibaba.nacos.sys.file.FileWatcher;
import com.alibaba.nacos.sys.file.WatchFileCenter;
import com.alibaba.nacos.sys.utils.DiskUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NacosAutoRefreshPropertySourceLoaderTest.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class NacosAutoRefreshPropertySourceLoaderTest {
@Autowired
private ConfigurableEnvironment environment;
private static String oldConfPath = "";
@BeforeClass
public static void before() throws URISyntaxException {
oldConfPath = EnvUtil.getConfPath();
EnvUtil.setConfPath(new File(ClassLoader.getSystemResource("application.properties").toURI()).getParent());
}
@AfterClass
public static void after() {
EnvUtil.setConfPath(oldConfPath);
}
@Test
public void testConfigFileAutoRefresh() throws URISyntaxException, InterruptedException, NacosException, IOException {
final URL url = ClassLoader.getSystemResource("application.properties");
EnvUtil.setContextPath(url.getPath());
final String val1 = environment.getProperty("name");
Assert.assertEquals("test-1", val1);
final File file = new File(url.toURI());
final String newKey = "nacos.config.refresh-" + System.currentTimeMillis();
final String newVal = System.currentTimeMillis() + "-lessspring";
DiskUtils.writeFile(file, ByteUtils.toBytes("\n" + newKey + "=" + newVal), true);
CountDownLatch latch = new CountDownLatch(1);
WatchFileCenter.registerWatcher(EnvUtil.getConfPath(), new FileWatcher() {
@Override
public void onChange(FileChangeEvent event) {
latch.countDown();
}
@Override
public boolean interest(String context) {
return StringUtils.contains(context, "application.properties");
}
});
latch.await();
ThreadUtils.sleep(10_000);
final String val2 = environment.getProperty(newKey);
Assert.assertEquals(newVal, val2);
}
}

View File

@ -33,6 +33,7 @@ import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
@ -73,26 +74,40 @@ public class User_ITCase extends HttpClient4Test {
HttpMethod.DELETE);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// Delete a user:
request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username2")
.appendParam("accessToken", accessToken)
.done(),
String.class,
HttpMethod.DELETE);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
}
@Test
public void login() {
ResponseEntity<String> response = request("/nacos/v1/auth/users/login",
Params.newParams()
.appendParam("username", "nacos")
.appendParam("password", "nacos")
.done(),
String.class,
HttpMethod.POST);
ResponseEntity<String> response = login("nacos", "nacos");
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
JsonNode json = JacksonUtils.toObj(response.getBody());
Assert.assertTrue(json.has("accessToken"));
accessToken = json.get("accessToken").textValue();
}
private ResponseEntity<String> login(String username,String password){
return request("/nacos/v1/auth/users/login",
Params.newParams()
.appendParam("username", username)
.appendParam("password", password)
.done(),
String.class,
HttpMethod.POST);
}
@Test
public void createUpdateDeleteUser() {
@ -211,4 +226,77 @@ public class User_ITCase extends HttpClient4Test {
}
Assert.assertFalse(found);
}
@Test
public void updateUserWithPermission() {
System.setProperty("nacos.core.auth.enabled", "true");
// admin login
login();
// create username1
ResponseEntity<String> response = request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username1")
.appendParam("password", "password1")
.appendParam("accessToken", accessToken)
.done(),
String.class,
HttpMethod.POST);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// create username2
response= request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username2")
.appendParam("password", "password2")
.appendParam("accessToken", accessToken)
.done(),
String.class,
HttpMethod.POST);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// user login
response = login("username1", "password1");
String user1AccessToken = JacksonUtils.toObj(response.getBody()).get("accessToken").textValue();
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
response = login("username2", "password2");
String user2AccessToken = JacksonUtils.toObj(response.getBody()).get("accessToken").textValue();
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// update by admin
response = request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username1")
.appendParam("newPassword", "password3")
.appendParam("accessToken", accessToken)
.done(),
String.class,
HttpMethod.PUT);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// update by same user
response = request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username1")
.appendParam("newPassword", "password4")
.appendParam("accessToken", user1AccessToken)
.done(),
String.class,
HttpMethod.PUT);
Assert.assertTrue(response.getStatusCode().is2xxSuccessful());
// update by another user
response = request("/nacos/v1/auth/users",
Params.newParams()
.appendParam("username", "username1")
.appendParam("newPassword", "password5")
.appendParam("accessToken", user2AccessToken)
.done(),
String.class,
HttpMethod.PUT);
Assert.assertEquals(response.getStatusCode(), HttpStatus.FORBIDDEN);
System.setProperty("nacos.core.auth.enabled", "false");
}
}

View File

@ -21,7 +21,7 @@ import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.core.cluster.Member;
import com.alibaba.nacos.core.cluster.MembersChangeEvent;
import com.alibaba.nacos.core.cluster.MemberUtils;
import com.alibaba.nacos.core.cluster.MemberUtil;
import com.alibaba.nacos.core.cluster.NodeState;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.sys.env.EnvUtil;
@ -97,7 +97,7 @@ public class ServerMemberManager_ITCase {
List<Member> members = new ArrayList<Member>(map.values());
Collections.sort(members);
List<String> ss = MemberUtils.simpleMembers(members);
List<String> ss = MemberUtil.simpleMembers(members);
Assert.assertEquals(ss.get(0), members.get(0).getAddress());
}
@ -145,7 +145,7 @@ public class ServerMemberManager_ITCase {
@Override
public void onEvent(MembersChangeEvent event) {
System.out.println(event);
healthMembers.set(MemberUtils.selectTargetMembers(event.getMembers(), member -> !NodeState.DOWN.equals(member.getState())));
healthMembers.set(MemberUtil.selectTargetMembers(event.getMembers(), member -> !NodeState.DOWN.equals(member.getState())));
if (first.getCount() == 1) {
first.countDown();
return;