diff --git a/sentinel-dashboard/.gitlab-ci.yml b/sentinel-dashboard/.gitlab-ci.yml new file mode 100644 index 0000000000..634af063e4 --- /dev/null +++ b/sentinel-dashboard/.gitlab-ci.yml @@ -0,0 +1,71 @@ +.default: &default + only: + - merge_requests + - web + +.default_tag: &default_tag + tags: + - backend + +stages: + - build + - quality + - deploy + - merge + - release + +merge_ready: +# tags: +# - backend + stage: quality + <<: *default + <<: *default_tag + script: + - mvn test -U -Pquality_check + + +post_merge: + stage: merge + <<: *default_tag + script: + - mvn clean test -U -Pquality_check + only: + refs: + - develop + +build_snapshot: +# tags: +# - backend + stage: build + <<: *default + <<: *default_tag + script: + - mvn clean install -DskipTests=true -U -Pdocker + +deploy_snapshot: +# tags: +# - backend + stage: deploy + <<: *default_tag + only: + - web + script: + - mvn deploy -DskipTests=true -Pdocker + when: manual + allow_failure: false + + +release: + <<: *default_tag + stage: release + script: + - git remote set-url origin "git@gitlab.phonepe.com:${CI_PROJECT_PATH}.git" + - git checkout master + - git checkout develop + - mvn -U -Pdocker jgitflow:release-start jgitflow:release-finish + - git push --all + - git push --tags origin + when: manual + only: + refs: + - develop diff --git a/sentinel-dashboard/Dockerfile b/sentinel-dashboard/Dockerfile new file mode 100644 index 0000000000..aafbccf8e0 --- /dev/null +++ b/sentinel-dashboard/Dockerfile @@ -0,0 +1,13 @@ +FROM openjdk:8-jdk-alpine + +ARG SENTINEL_VERSION="1.7.1" + +WORKDIR /home/sentinel + +ENV JAR_FILE sentinel-dashboard.jar +ADD target/sentinel-dashboard.jar ${JAR_FILE} + +EXPOSE 7070 +EXPOSE 7071 + +CMD java -Dserver.port=7070 -Dcsp.sentinel.dashboard.server=localhost:7070 -Dproject.name=sentinel -jar ${JAR_FILE} \ No newline at end of file diff --git a/sentinel-dashboard/pom.xml b/sentinel-dashboard/pom.xml index bcf0b2a94d..730988e337 100755 --- a/sentinel-dashboard/pom.xml +++ b/sentinel-dashboard/pom.xml @@ -11,37 +11,91 @@ sentinel-dashboard jar + 1.7.1-SNAPSHOT + + + + clojars + Clojars repository + https://clojars.org/repo + + + phonepe-snapshots + http://artifactory.phonepe.com/content/repositories/snapshots + + 1.8 1.8 2.0.5.RELEASE 4.0.1 + 1.7.1 com.alibaba.csp sentinel-core + 1.7.1 com.alibaba.csp sentinel-web-servlet - ${project.version} + 1.7.1 + + + com.alibaba.csp + sentinel-core + + com.alibaba.csp sentinel-transport-simple-http + 1.7.1 + + + com.alibaba.csp + sentinel-transport-common + + + + + com.alibaba.csp + sentinel-transport-common + 1.7.1 + + + com.alibaba.csp + sentinel-datasource-extension + 1.7.1 com.alibaba.csp sentinel-parameter-flow-control - ${project.version} + 1.7.1 + + + com.alibaba.csp + sentinel-core + + com.alibaba.csp sentinel-api-gateway-adapter-common - ${project.version} + 1.7.1 + + + com.alibaba.csp + sentinel-core + + + com.alibaba.csp + sentinel-parameter-flow-control + + @@ -102,7 +156,14 @@ com.alibaba.csp sentinel-datasource-nacos + 1.7.1 test + + + com.alibaba.csp + sentinel-datasource-extension + + @@ -117,7 +178,6 @@ org.apache.curator curator-recipes ${curator.version} - test @@ -136,6 +196,25 @@ 1.16.1 test + + + org.apache.curator + curator-framework + 4.2.0 + + + + com.alibaba.csp + sentinel-datasource-zookeeper + 1.7.1 + + + com.alibaba.csp + sentinel-datasource-extension + + + + @@ -171,9 +250,9 @@ org.apache.maven.plugins maven-deploy-plugin ${maven.deploy.version} - + diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java index 5d9599ee96..63225c2b17 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/auth/SimpleWebAuthServiceImpl.java @@ -15,32 +15,34 @@ */ package com.alibaba.csp.sentinel.dashboard.auth; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; /** * @author cdfive * @since 1.6.0 */ -@Component @Primary -@ConditionalOnProperty(name = "auth.enabled", matchIfMissing = true) +@Component public class SimpleWebAuthServiceImpl implements AuthService { - public static final String WEB_SESSION_KEY = "session_sentinel_admin"; + public static final String WEB_SESSION_KEY = "session_sentinel_user"; + public static final String WEB_SESSION_KEY_ADMIN = "session_sentinel_admin"; @Override public AuthUser getAuthUser(HttpServletRequest request) { HttpSession session = request.getSession(); - Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY); - if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) { + Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY_ADMIN); + + if (sentinelUserObj != null && sentinelUserObj instanceof AuthService.AuthUser) { + return (AuthUser) sentinelUserObj; + } + sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY); + if (sentinelUserObj != null && sentinelUserObj instanceof AuthService.AuthUser) { return (AuthUser) sentinelUserObj; } - return null; } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java index 008e67c1ec..4c2c920646 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java @@ -15,37 +15,9 @@ */ package com.alibaba.csp.sentinel.dashboard.client; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.command.CommandConstants; -import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.command.vo.NodeVo; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; -import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; -import com.alibaba.csp.sentinel.slots.block.Rule; -import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; -import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; -import com.alibaba.csp.sentinel.slots.system.SystemRule; -import com.alibaba.csp.sentinel.util.AssertUtil; -import com.alibaba.csp.sentinel.util.StringUtil; -import com.alibaba.fastjson.JSON; +import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; @@ -55,14 +27,36 @@ import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; import com.alibaba.csp.sentinel.dashboard.domain.cluster.ClusterClientInfoVO; -import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; -import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ClusterClientConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerFlowConfig; import com.alibaba.csp.sentinel.dashboard.domain.cluster.config.ServerTransportConfig; +import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterServerStateVO; +import com.alibaba.csp.sentinel.dashboard.domain.cluster.state.ClusterStateSimpleEntity; +import com.alibaba.csp.sentinel.dashboard.util.AsyncUtils; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; - -import org.apache.http.Consts; +import com.alibaba.csp.sentinel.slots.block.Rule; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.slots.system.SystemRule; +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.fastjson.JSON; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; @@ -112,13 +106,6 @@ public class SentinelApiClient { private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig"; private static final String MODIFY_CLUSTER_SERVER_FLOW_CONFIG_PATH = "cluster/server/modifyFlowConfig"; private static final String MODIFY_CLUSTER_SERVER_NAMESPACE_SET_PATH = "cluster/server/modifyNamespaceSet"; - - private static final String FETCH_GATEWAY_API_PATH = "gateway/getApiDefinitions"; - private static final String MODIFY_GATEWAY_API_PATH = "gateway/updateApiDefinitions"; - - private static final String FETCH_GATEWAY_FLOW_RULE_PATH = "gateway/getRules"; - private static final String MODIFY_GATEWAY_FLOW_RULE_PATH = "gateway/updateRules"; - private static final String FLOW_RULE_TYPE = "flow"; private static final String DEGRADE_RULE_TYPE = "degrade"; private static final String SYSTEM_RULE_TYPE = "system"; @@ -127,7 +114,7 @@ public class SentinelApiClient { private CloseableHttpAsyncClient httpClient; private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0); - + @Autowired private AppManagement appManagement; @@ -146,11 +133,11 @@ protected boolean isRedirectable(final String method) { private boolean isSuccess(int statusCode) { return statusCode >= 200 && statusCode < 300; } - + private boolean isCommandNotFound(int statusCode, String body) { return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX); } - + private StringBuilder queryString(Map params) { StringBuilder queryStringBuilder = new StringBuilder(); for (Entry entry : params.entrySet()) { @@ -168,7 +155,7 @@ private StringBuilder queryString(Map params) { } return queryStringBuilder; } - + private HttpUriRequest postRequest(String url, Map params) { HttpPost httpPost = new HttpPost(url); if (params != null && params.size() > 0) { @@ -176,11 +163,16 @@ private HttpUriRequest postRequest(String url, Map params) { for (Entry entry : params.entrySet()) { list.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } - httpPost.setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8)); + try { + httpPost.setEntity(new UrlEncodedFormEntity(list)); + } catch (UnsupportedEncodingException e) { + logger.warn("httpPostContent encode entity error: {}", params, e); + return null; + } } return httpPost; } - + private String urlEncode(String str) { try { return URLEncoder.encode(str, DEFAULT_CHARSET.name()); @@ -189,7 +181,7 @@ private String urlEncode(String str) { return null; } } - + private String getBody(HttpResponse response) throws Exception { Charset charset = null; try { @@ -202,10 +194,10 @@ private String getBody(HttpResponse response) throws Exception { } return EntityUtils.toString(response.getEntity(), charset != null ? charset : DEFAULT_CHARSET); } - + /** * With no param - * + * * @param ip * @param port * @param api @@ -214,10 +206,10 @@ private String getBody(HttpResponse response) throws Exception { private CompletableFuture executeCommand(String ip, int port, String api, boolean useHttpPost) { return executeCommand(ip, port, api, null, useHttpPost); } - + /** * No app specified, force to GET - * + * * @param ip * @param port * @param api @@ -230,7 +222,7 @@ private CompletableFuture executeCommand(String ip, int port, String api /** * Prefer to execute request using POST - * + * * @param app * @param ip * @param port @@ -271,7 +263,7 @@ private CompletableFuture executeCommand(String app, String ip, int port return executeCommand(postRequest(urlBuilder.toString(), params)); } } - + private CompletableFuture executeCommand(HttpUriRequest request) { CompletableFuture future = new CompletableFuture<>(); httpClient.execute(request, new FutureCallback() { @@ -309,11 +301,11 @@ public void cancelled() { }); return future; } - + public void close() throws Exception { httpClient.close(); } - + @Nullable private CompletableFuture> fetchItemsAsync(String ip, int port, String api, String type, Class ruleType) { AssertUtil.notEmpty(ip, "Bad machine IP"); @@ -326,7 +318,7 @@ private CompletableFuture> fetchItemsAsync(String ip, int port, Stri return executeCommand(ip, port, api, params, false) .thenApply(json -> JSON.parseArray(json, ruleType)); } - + @Nullable private List fetchItems(String ip, int port, String api, String type, Class ruleType) { try { @@ -346,11 +338,11 @@ private List fetchItems(String ip, int port, String api, String type, Cla return null; } } - + private List fetchRules(String ip, int port, String type, Class ruleType) { return fetchItems(ip, port, GET_RULES_PATH, type, ruleType); } - + private boolean setRules(String app, String ip, int port, String type, List entities) { if (entities == null) { return true; @@ -379,30 +371,6 @@ private boolean setRules(String app, String ip, int port, String type, List setRulesAsync(String app, String ip, int port, String type, List entities) { - try { - AssertUtil.notNull(entities, "rules cannot be null"); - AssertUtil.notEmpty(app, "Bad app name"); - AssertUtil.notEmpty(ip, "Bad machine IP"); - AssertUtil.isTrue(port > 0, "Bad machine port"); - String data = JSON.toJSONString( - entities.stream().map(r -> r.toRule()).collect(Collectors.toList())); - Map params = new HashMap<>(2); - params.put("type", type); - params.put("data", data); - return executeCommand(app, ip, port, SET_RULES_PATH, params, true) - .thenCompose(r -> { - if ("success".equalsIgnoreCase(r.trim())) { - return CompletableFuture.completedFuture(null); - } - return AsyncUtils.newFailedFuture(new CommandFailedException(r)); - }); - } catch (Exception e) { - logger.error("setRulesAsync API failed, type={}", type, e); - return AsyncUtils.newFailedFuture(e); - } - } - public List fetchResourceOfMachine(String ip, int port, String type) { return fetchItems(ip, port, RESOURCE_URL_PATH, type, NodeVo.class); } @@ -514,10 +482,6 @@ public boolean setFlowRuleOfMachine(String app, String ip, int port, List setFlowRuleOfMachineAsync(String app, String ip, int port, List rules) { - return setRulesAsync(app, ip, port, FLOW_RULE_TYPE, rules); - } - /** * set rules of the machine. rules == null will return immediately; * rules.isEmpty() means setting the rules to empty. @@ -729,90 +693,4 @@ public CompletableFuture fetchClusterServerBasicInfo(Strin return AsyncUtils.newFailedFuture(ex); } } - - public CompletableFuture> fetchApis(String app, String ip, int port) { - if (StringUtil.isBlank(ip) || port <= 0) { - return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); - } - - try { - return executeCommand(ip, port, FETCH_GATEWAY_API_PATH, false) - .thenApply(r -> { - List entities = JSON.parseArray(r, ApiDefinitionEntity.class); - if (entities != null) { - for (ApiDefinitionEntity entity : entities) { - entity.setApp(app); - entity.setIp(ip); - entity.setPort(port); - } - } - return entities; - }); - } catch (Exception ex) { - logger.warn("Error when fetching gateway apis", ex); - return AsyncUtils.newFailedFuture(ex); - } - } - - public boolean modifyApis(String app, String ip, int port, List apis) { - if (apis == null) { - return true; - } - - try { - AssertUtil.notEmpty(app, "Bad app name"); - AssertUtil.notEmpty(ip, "Bad machine IP"); - AssertUtil.isTrue(port > 0, "Bad machine port"); - String data = JSON.toJSONString( - apis.stream().map(r -> r.toApiDefinition()).collect(Collectors.toList())); - Map params = new HashMap<>(2); - params.put("data", data); - String result = executeCommand(app, ip, port, MODIFY_GATEWAY_API_PATH, params, true).get(); - logger.info("Modify gateway apis: {}", result); - return true; - } catch (Exception e) { - logger.warn("Error when modifying gateway apis", e); - return false; - } - } - - public CompletableFuture> fetchGatewayFlowRules(String app, String ip, int port) { - if (StringUtil.isBlank(ip) || port <= 0) { - return AsyncUtils.newFailedFuture(new IllegalArgumentException("Invalid parameter")); - } - - try { - return executeCommand(ip, port, FETCH_GATEWAY_FLOW_RULE_PATH, false) - .thenApply(r -> { - List gatewayFlowRules = JSON.parseArray(r, GatewayFlowRule.class); - List entities = gatewayFlowRules.stream().map(rule -> GatewayFlowRuleEntity.fromGatewayFlowRule(app, ip, port, rule)).collect(Collectors.toList()); - return entities; - }); - } catch (Exception ex) { - logger.warn("Error when fetching gateway flow rules", ex); - return AsyncUtils.newFailedFuture(ex); - } - } - - public boolean modifyGatewayFlowRules(String app, String ip, int port, List rules) { - if (rules == null) { - return true; - } - - try { - AssertUtil.notEmpty(app, "Bad app name"); - AssertUtil.notEmpty(ip, "Bad machine IP"); - AssertUtil.isTrue(port > 0, "Bad machine port"); - String data = JSON.toJSONString( - rules.stream().map(r -> r.toGatewayFlowRule()).collect(Collectors.toList())); - Map params = new HashMap<>(2); - params.put("data", data); - String result = executeCommand(app, ip, port, MODIFY_GATEWAY_FLOW_RULE_PATH, params, true).get(); - logger.info("Modify gateway flow rules: {}", result); - return true; - } catch (Exception e) { - logger.warn("Error when modifying gateway apis", e); - return false; - } - } } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java index 92e0608ec3..ef53efc919 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/DashboardConfig.java @@ -47,6 +47,10 @@ public class DashboardConfig { */ public static final String CONFIG_AUTH_PASSWORD = "sentinel.dashboard.auth.password"; + public static final String ADMIN_USERNAME = "sentinel.dashboard.auth.admin.username"; + + public static final String ADMIN_PASSWORD = "sentinel.dashboard.auth.admin.password"; + /** * Hide application name in sidebar when it has no healthy machines after specific period in millisecond. */ @@ -65,7 +69,7 @@ public class DashboardConfig { public static final String CONFIG_AUTO_REMOVE_MACHINE_MILLIS = "sentinel.dashboard.autoRemoveMachineMillis"; private static final ConcurrentMap cacheMap = new ConcurrentHashMap<>(); - + @NonNull private static String getConfig(String name) { // env @@ -121,19 +125,19 @@ public static String getAuthPassword() { public static int getHideAppNoMachineMillis() { return getConfigInt(CONFIG_HIDE_APP_NO_MACHINE_MILLIS, 0, 60000); } - + public static int getRemoveAppNoMachineMillis() { return getConfigInt(CONFIG_REMOVE_APP_NO_MACHINE_MILLIS, 0, 120000); } - + public static int getAutoRemoveMachineMillis() { return getConfigInt(CONFIG_AUTO_REMOVE_MACHINE_MILLIS, 0, 300000); } - + public static int getUnhealthyMachineMillis() { return getConfigInt(CONFIG_UNHEALTHY_MACHINE_MILLIS, DEFAULT_MACHINE_HEALTHY_TIMEOUT_MS, 30000); } - + public static void clearCache() { cacheMap.clear(); } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java index 92e51e54ae..13aaa6019a 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/config/WebConfig.java @@ -25,6 +25,8 @@ import com.alibaba.csp.sentinel.dashboard.auth.LoginAuthenticationFilter; import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.dashboard.filter.AuthFilter; +import javax.servlet.Filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -48,15 +50,7 @@ public class WebConfig implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebConfig.class); @Autowired - private LoginAuthenticationFilter loginAuthenticationFilter; - - @Autowired - private AuthorizationInterceptor authorizationInterceptor; - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(authorizationInterceptor).addPathPatterns("/**"); - } + private AuthFilter authFilter; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { @@ -79,35 +73,16 @@ public FilterRegistrationBean sentinelFilterRegistration() { registration.addUrlPatterns("/*"); registration.setName("sentinelFilter"); registration.setOrder(1); - // If this is enabled, the entrance of all Web URL resources will be unified as a single context name. - // In most scenarios that's enough, and it could reduce the memory footprint. - registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "true"); logger.info("Sentinel servlet CommonFilter registered"); return registration; } - @PostConstruct - public void doInit() { - Set suffixSet = new HashSet<>(Arrays.asList(".js", ".css", ".html", ".ico", ".txt", - ".woff", ".woff2")); - // Example: register a UrlCleaner to exclude URLs of common static resources. - WebCallbackManager.setUrlCleaner(url -> { - if (StringUtil.isEmpty(url)) { - return url; - } - if (suffixSet.stream().anyMatch(url::endsWith)) { - return null; - } - return url; - }); - } - @Bean public FilterRegistrationBean authenticationFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(loginAuthenticationFilter); + registration.setFilter(authFilter); registration.addUrlPatterns("/*"); registration.setName("authenticationFilter"); registration.setOrder(0); diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java index 635489661e..ce2c776a75 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AppController.java @@ -66,7 +66,7 @@ public Result> getMachinesByApp(@PathVariable("app") String Collections.sort(list, Comparator.comparing(MachineInfo::getApp).thenComparing(MachineInfo::getIp).thenComparingInt(MachineInfo::getPort)); return Result.ofSuccess(MachineInfoVo.fromMachineInfoList(list)); } - + @RequestMapping(value = "/{app}/machine/remove.json") public Result removeMachineById( @PathVariable("app") String app, diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java index 8b01aed232..118d0ffee7 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthController.java @@ -16,20 +16,20 @@ package com.alibaba.csp.sentinel.dashboard.controller; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.SimpleWebAuthServiceImpl; import com.alibaba.csp.sentinel.dashboard.config.DashboardConfig; import com.alibaba.csp.sentinel.dashboard.domain.Result; +import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import javax.servlet.http.HttpServletRequest; - /** * @author cdfive * @since 1.6.0 @@ -46,11 +46,14 @@ public class AuthController { @Value("${auth.password:sentinel}") private String authPassword; - @Autowired - private AuthService authService; + @Value("${auth.admin.username}") + private String adminUsername; + + @Value("${auth.admin.password}") + private String adminPassword; @PostMapping("/login") - public Result login(HttpServletRequest request, String username, String password) { + public Result login(HttpServletRequest request, String username, String password) { if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) { authUsername = DashboardConfig.getAuthUsername(); } @@ -64,29 +67,40 @@ public Result login(HttpServletRequest request, String use * auth will pass, as the front side validate the input which can't be blank, * so user can input any username or password(both are not blank) to login in that case. */ - if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username) - || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) { - LOGGER.error("Login failed: Invalid username or password, username=" + username); - return Result.ofFail(-1, "Invalid username or password"); + if (StringUtils.isBlank(username)) { + LOGGER.error("Login failed: Invalid username is null"); + return Result.ofFail(-1, "用户名不能为空!"); + } + if (!authUsername.equals(username) && !adminUsername.equals(username)) { + LOGGER.error("Login failed: 用户名不正确"); + return Result.ofFail(-1, "用户名不正确!"); + } + if (adminUsername.equals(username)) { + if (StringUtils.isNotBlank(adminPassword) && !adminPassword.equals(password)) { + LOGGER.error("Login failed: 密码不正确, username=" + username); + return Result.ofFail(-1, "密码不正确"); + } + AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); + request.getSession() + .setAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY_ADMIN, authUser); + return Result.ofSuccess(authUser); + } else { + if (StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) { + LOGGER.error("Login failed: Invalid username or password, username=" + username); + return Result.ofFail(-1, "密码不正确"); + } + AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); + request.getSession() + .setAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY, authUser); + return Result.ofSuccess(authUser); } - AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); - request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY, authUser); - return Result.ofSuccess(authUser); } - @PostMapping(value = "/logout") + @RequestMapping(value = "/logout", method = RequestMethod.POST) public Result logout(HttpServletRequest request) { - request.getSession().invalidate(); + request.getSession() + .invalidate(); return Result.ofSuccess(null); } - - @PostMapping(value = "/check") - public Result check(HttpServletRequest request) { - AuthService.AuthUser authUser = authService.getAuthUser(request); - if (authUser == null) { - return Result.ofFail(-1, "Not logged in"); - } - return Result.ofSuccess(authUser); - } } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java index 294455f02a..c5dcfbca32 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/AuthorityRuleController.java @@ -15,23 +15,23 @@ */ package com.alibaba.csp.sentinel.dashboard.controller; -import java.util.Date; -import java.util.List; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.util.StringUtil; - +import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity; +import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; - +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.util.StringUtil; +import java.util.Date; +import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -57,11 +57,17 @@ public class AuthorityRuleController { @Autowired private RuleRepository repository; + @Autowired + private AuthService authService; + + @Value("${auth.admin.username}") + private String adminUsername; + @GetMapping("/rules") - @AuthAction(PrivilegeType.READ_RULE) - public Result> apiQueryAllRulesForMachine(@RequestParam String app, - @RequestParam String ip, - @RequestParam Integer port) { + public Result> apiQueryAllRulesForMachine(HttpServletRequest request, + @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } @@ -104,15 +110,20 @@ private Result checkEntityInternal(AuthorityRuleEntity entity) { return Result.ofFail(-1, "limitApp should be valid"); } if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE - && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) { + && entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) { return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)"); } return null; } @PostMapping("/rule") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) { + public Result apiAddAuthorityRule(HttpServletRequest request, + @RequestBody AuthorityRuleEntity entity) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; @@ -134,9 +145,13 @@ public Result apiAddAuthorityRule(@RequestBody AuthorityRul } @PutMapping("/rule/{id}") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiUpdateParamFlowRule(@PathVariable("id") Long id, - @RequestBody AuthorityRuleEntity entity) { + public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, + @RequestBody AuthorityRuleEntity entity) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } @@ -164,8 +179,11 @@ public Result apiUpdateParamFlowRule(@PathVariable("id") Lo } @DeleteMapping("/rule/{id}") - @AuthAction(PrivilegeType.DELETE_RULE) - public Result apiDeleteRule(@PathVariable("id") Long id) { + public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id cannot be null"); } @@ -173,6 +191,7 @@ public Result apiDeleteRule(@PathVariable("id") Long id) { if (oldEntity == null) { return Result.ofSuccess(null); } + authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java index 61aaee68aa..d337114294 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/DegradeController.java @@ -15,30 +15,34 @@ */ package com.alibaba.csp.sentinel.dashboard.controller; -import java.util.Date; -import java.util.List; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.util.StringUtil; - +import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore; - +import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; +import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.util.StringUtil; +import java.util.Date; +import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** - * @author leyou + * Degrade rule controller + * + * @author guifei.qin */ @Controller @RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE) @@ -51,10 +55,25 @@ public class DegradeController { @Autowired private SentinelApiClient sentinelApiClient; + @Autowired + private AuthService authService; + + @Autowired + @Qualifier("degradeRuleZookeeperProvider") + private DynamicRuleProvider> ruleProvider; + @Autowired + @Qualifier("degradeRuleZookeeperPublisher") + private DynamicRulePublisher> rulePublisher; + + @Value("${auth.admin.username}") + private String adminUsername; + @ResponseBody @RequestMapping("/rules.json") - @AuthAction(PrivilegeType.READ_RULE) - public Result> queryMachineRules(String app, String ip, Integer port) { + public Result> queryMachineRules(HttpServletRequest request, String app, String ip, + Integer port) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); @@ -66,7 +85,14 @@ public Result> queryMachineRules(String app, String ip, return Result.ofFail(-1, "port can't be null"); } try { - List rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port); +// List rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port); + List rules = ruleProvider.getRules(app); + if (rules != null && !rules.isEmpty()) { + for (DegradeRuleEntity entity : rules) { + entity.setApp(app); + } + } + rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { @@ -77,9 +103,14 @@ public Result> queryMachineRules(String app, String ip, @ResponseBody @RequestMapping("/new.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result add(String app, String ip, Integer port, String limitApp, String resource, - Double count, Integer timeWindow, Integer grade) { + public Result add(HttpServletRequest request, String app, String ip, Integer port, + String limitApp, String resource, Double count, Integer timeWindow, Integer grade) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } + authUser.authTarget(app, PrivilegeType.WRITE_RULE); + if (StringUtil.isBlank(app)) { return Result.ofFail(-1, "app can't be null or empty"); } @@ -121,21 +152,23 @@ public Result add(String app, String ip, Integer port, String entity.setGmtModified(date); try { entity = repository.save(entity); + publishRules(app); } catch (Throwable throwable) { logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } - if (!publishRules(app, ip, port)) { - logger.info("publish degrade rules fail after rule add"); - } + return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/save.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result updateIfNotNull(Long id, String app, String limitApp, String resource, - Double count, Integer timeWindow, Integer grade) { + public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, + String resource, Double count, Integer timeWindow, Integer grade) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -148,7 +181,7 @@ public Result updateIfNotNull(Long id, String app, String lim if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } - + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } @@ -172,20 +205,24 @@ public Result updateIfNotNull(Long id, String app, String lim entity.setGmtModified(date); try { entity = repository.save(entity); + if (entity == null) { + return Result.ofFail(-1, "save entity fail"); + } + publishRules(entity.getApp()); } catch (Throwable throwable) { logger.error("save error:", throwable); return Result.ofThrowable(-1, throwable); } - if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { - logger.info("publish degrade rules fail after rule update"); - } return Result.ofSuccess(entity); } @ResponseBody @RequestMapping("/delete.json") - @AuthAction(PrivilegeType.DELETE_RULE) - public Result delete(Long id) { + public Result delete(HttpServletRequest request, Long id) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -194,21 +231,24 @@ public Result delete(Long id) { if (oldEntity == null) { return Result.ofSuccess(null); } - + authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); + publishRules(oldEntity.getApp()); } catch (Throwable throwable) { logger.error("delete error:", throwable); return Result.ofThrowable(-1, throwable); } - if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { - logger.info("publish degrade rules fail after rule delete"); - } return Result.ofSuccess(id); } - private boolean publishRules(String app, String ip, Integer port) { - List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); - return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); +// private boolean publishRules(String app, String ip, Integer port) { +// List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); +// return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules); +// } + + private void publishRules(/*@NonNull*/ String app) throws Exception { + List rules = repository.findAllByApp(app); + rulePublisher.publish(app, rules); } } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java index 50c4e32f66..803a68ffe0 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/FlowControllerV1.java @@ -15,22 +15,18 @@ */ package com.alibaba.csp.sentinel.dashboard.controller; -import java.util.Date; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; -import com.alibaba.csp.sentinel.util.StringUtil; - import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter; - +import com.alibaba.csp.sentinel.util.StringUtil; +import java.util.Date; +import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -57,15 +53,17 @@ public class FlowControllerV1 { @Autowired private InMemoryRuleRepositoryAdapter repository; + @Autowired + private AuthService authService; @Autowired private SentinelApiClient sentinelApiClient; @GetMapping("/rules") - @AuthAction(PrivilegeType.READ_RULE) - public Result> apiQueryMachineRules(@RequestParam String app, - @RequestParam String ip, - @RequestParam Integer port) { + public Result> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app, + @RequestParam String ip, @RequestParam Integer port) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); @@ -134,8 +132,10 @@ private Result checkEntityInternal(FlowRuleEntity entity) { } @PostMapping("/rule") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiAddFlowRule(@RequestBody FlowRuleEntity entity) { + public Result apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); + Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; @@ -148,23 +148,23 @@ public Result apiAddFlowRule(@RequestBody FlowRuleEntity entity) entity.setResource(entity.getResource().trim()); try { entity = repository.save(entity); - - publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS); - return Result.ofSuccess(entity); - } catch (Throwable t) { - Throwable e = t instanceof ExecutionException ? t.getCause() : t; - logger.error("Failed to add new flow rule, app={}, ip={}", entity.getApp(), entity.getIp(), e); - return Result.ofFail(-1, e.getMessage()); + } catch (Throwable throwable) { + logger.error("Failed to add flow rule", throwable); + return Result.ofThrowable(-1, throwable); } + if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { + logger.error("Publish flow rules failed after rule add"); + } + return Result.ofSuccess(entity); } @PutMapping("/save.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiUpdateFlowRule(Long id, String app, - String limitApp, String resource, Integer grade, - Double count, Integer strategy, String refResource, - Integer controlBehavior, Integer warmUpPeriodSec, - Integer maxQueueingTimeMs) { + public Result updateIfNotNull(HttpServletRequest request, Long id, String app, String limitApp, + String resource, Integer grade, Double count, Integer strategy, String refResource, Integer controlBehavior, + Integer warmUpPeriodSec, Integer maxQueueingTimeMs) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.WRITE_RULE); + if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -225,23 +225,21 @@ public Result apiUpdateFlowRule(Long id, String app, try { entity = repository.save(entity); if (entity == null) { - return Result.ofFail(-1, "save entity fail: null"); + return Result.ofFail(-1, "save entity fail"); } - - publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get(5000, TimeUnit.MILLISECONDS); - return Result.ofSuccess(entity); - } catch (Throwable t) { - Throwable e = t instanceof ExecutionException ? t.getCause() : t; - logger.error("Error when updating flow rules, app={}, ip={}, ruleId={}", entity.getApp(), - entity.getIp(), id, e); - return Result.ofFail(-1, e.getMessage()); + } catch (Throwable throwable) { + logger.error("save error:", throwable); + return Result.ofThrowable(-1, throwable); + } + if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) { + logger.info("publish flow rules fail after rule update"); } + return Result.ofSuccess(entity); } @DeleteMapping("/delete.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiDeleteFlowRule(Long id) { - + public Result delete(HttpServletRequest request, Long id) { + AuthUser authUser = authService.getAuthUser(request); if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -249,25 +247,20 @@ public Result apiDeleteFlowRule(Long id) { if (oldEntity == null) { return Result.ofSuccess(null); } - + authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Exception e) { return Result.ofFail(-1, e.getMessage()); } - try { - publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(5000, TimeUnit.MILLISECONDS); - return Result.ofSuccess(id); - } catch (Throwable t) { - Throwable e = t instanceof ExecutionException ? t.getCause() : t; - logger.error("Error when deleting flow rules, app={}, ip={}, id={}", oldEntity.getApp(), - oldEntity.getIp(), id, e); - return Result.ofFail(-1, e.getMessage()); + if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { + logger.info("publish flow rules fail after rule delete"); } + return Result.ofSuccess(id); } - private CompletableFuture publishRules(String app, String ip, Integer port) { + private boolean publishRules(String app, String ip, Integer port) { List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); - return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules); + return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules); } } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java index 9a9a7f76a4..98ad3434ad 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/MachineRegistryController.java @@ -60,7 +60,6 @@ public Result receiveHeartBeat(String app, @RequestParam(value = "app_type", try { MachineInfo machineInfo = new MachineInfo(); machineInfo.setApp(app); - machineInfo.setAppType(appType); machineInfo.setHostname(hostname); machineInfo.setIp(ip); machineInfo.setPort(port); diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java index 4039ca6746..a2543280f2 100644 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/ParamFlowRuleController.java @@ -15,30 +15,30 @@ */ package com.alibaba.csp.sentinel.dashboard.controller; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; -import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; -import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException; +import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity; +import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; +import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; import com.alibaba.csp.sentinel.dashboard.domain.Result; import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; import com.alibaba.csp.sentinel.dashboard.util.VersionUtils; - +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.util.StringUtil; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -65,6 +65,10 @@ public class ParamFlowRuleController { private AppManagement appManagement; @Autowired private RuleRepository repository; + @Autowired + private AuthService authService; + @Value("${auth.admin.username}") + private String adminUsername; private boolean checkIfSupported(String app, String ip, int port) { try { @@ -80,10 +84,10 @@ private boolean checkIfSupported(String app, String ip, int port) { } @GetMapping("/rules") - @AuthAction(PrivilegeType.READ_RULE) - public Result> apiQueryAllRulesForMachine(@RequestParam String app, - @RequestParam String ip, - @RequestParam Integer port) { + public Result> apiQueryAllRulesForMachine(HttpServletRequest request, + @RequestParam String app, @RequestParam String ip, @RequestParam Integer port) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app cannot be null or empty"); } @@ -119,8 +123,13 @@ private boolean isNotSupported(Throwable ex) { } @PostMapping("/rule") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity) { + public Result apiAddParamFlowRule(HttpServletRequest request, + @RequestBody ParamFlowRuleEntity entity) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; @@ -188,9 +197,12 @@ private Result checkEntityInternal(ParamFlowRuleEntity entity) { } @PutMapping("/rule/{id}") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result apiUpdateParamFlowRule(@PathVariable("id") Long id, - @RequestBody ParamFlowRuleEntity entity) { + public Result apiUpdateParamFlowRule(HttpServletRequest request, @PathVariable("id") Long id, + @RequestBody ParamFlowRuleEntity entity) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null || id <= 0) { return Result.ofFail(-1, "Invalid id"); } @@ -198,7 +210,7 @@ public Result apiUpdateParamFlowRule(@PathVariable("id") Lo if (oldEntity == null) { return Result.ofFail(-1, "id " + id + " does not exist"); } - + authUser.authTarget(oldEntity.getApp(), PrivilegeType.WRITE_RULE); Result checkResult = checkEntityInternal(entity); if (checkResult != null) { return checkResult; @@ -228,8 +240,11 @@ public Result apiUpdateParamFlowRule(@PathVariable("id") Lo } @DeleteMapping("/rule/{id}") - @AuthAction(PrivilegeType.DELETE_RULE) - public Result apiDeleteRule(@PathVariable("id") Long id) { + public Result apiDeleteRule(HttpServletRequest request, @PathVariable("id") Long id) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id cannot be null"); } @@ -237,7 +252,7 @@ public Result apiDeleteRule(@PathVariable("id") Long id) { if (oldEntity == null) { return Result.ofSuccess(null); } - + authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get(); diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java index daa0b98b5a..c87ea41c6a 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java @@ -15,41 +15,51 @@ */ package com.alibaba.csp.sentinel.dashboard.controller; -import java.util.Date; -import java.util.List; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser; import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType; -import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository; -import com.alibaba.csp.sentinel.util.StringUtil; - +import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity; import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; import com.alibaba.csp.sentinel.dashboard.domain.Result; - +import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemSystemRuleStore; +import com.alibaba.csp.sentinel.util.StringUtil; +import java.util.Date; +import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.ResponseBody; /** * @author leyou(lihao) */ -@RestController -@RequestMapping("/system") +@Controller +@RequestMapping(value = "/system", produces = MediaType.APPLICATION_JSON_VALUE) public class SystemController { - private final Logger logger = LoggerFactory.getLogger(SystemController.class); + private static Logger logger = LoggerFactory.getLogger(SystemController.class); @Autowired - private RuleRepository repository; + private InMemSystemRuleStore repository; @Autowired private SentinelApiClient sentinelApiClient; + @Autowired + private AuthService authService; - private Result checkBasicParams(String app, String ip, Integer port) { + @Value("${auth.admin.username}") + private String adminUsername; + + @ResponseBody + @RequestMapping("/rules.json") + Result> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) { + AuthUser authUser = authService.getAuthUser(request); + authUser.authTarget(app, PrivilegeType.READ_RULE); if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } @@ -59,26 +69,12 @@ private Result checkBasicParams(String app, String ip, Integer port) { if (port == null) { return Result.ofFail(-1, "port can't be null"); } - if (port <= 0 || port > 65535) { - return Result.ofFail(-1, "port should be in (0, 65535)"); - } - return null; - } - - @GetMapping("/rules.json") - @AuthAction(PrivilegeType.READ_RULE) - public Result> apiQueryMachineRules(String app, String ip, - Integer port) { - Result> checkResult = checkBasicParams(app, ip, port); - if (checkResult != null) { - return checkResult; - } try { List rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port); rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { - logger.error("Query machine system rules error", throwable); + logger.error("queryApps error:", throwable); return Result.ofThrowable(-1, throwable); } } @@ -93,42 +89,40 @@ private int countNotNullAndNotNegative(Number... values) { return notNullCount; } + @ResponseBody @RequestMapping("/new.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiAdd(String app, String ip, Integer port, - Double highestSystemLoad, Double highestCpuUsage, Long avgRt, - Long maxThread, Double qps) { - - Result checkResult = checkBasicParams(app, ip, port); - if (checkResult != null) { - return checkResult; + Result add(HttpServletRequest request, String app, String ip, Integer port, Double avgLoad, Long avgRt, + Long maxThread, Double qps) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } + authUser.authTarget(app, PrivilegeType.WRITE_RULE); + if (StringUtil.isBlank(app)) { + return Result.ofFail(-1, "app can't be null or empty"); } - - int notNullCount = countNotNullAndNotNegative(highestSystemLoad, avgRt, maxThread, qps, highestCpuUsage); - if (notNullCount != 1) { - return Result.ofFail(-1, "only one of [highestSystemLoad, avgRt, maxThread, qps,highestCpuUsage] " - + "value must be set > 0, but " + notNullCount + " values get"); + if (StringUtil.isBlank(ip)) { + return Result.ofFail(-1, "ip can't be null or empty"); } - if (null != highestCpuUsage && highestCpuUsage > 1) { - return Result.ofFail(-1, "highestCpuUsage must between [0.0, 1.0]"); + if (port == null) { + return Result.ofFail(-1, "port can't be null"); + } + int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps); + if (notNullCount != 1) { + return Result.ofFail(-1, + "only one of [avgLoad, avgRt, maxThread, qps] " + "value must be set >= 0, but " + notNullCount + + " values get"); } SystemRuleEntity entity = new SystemRuleEntity(); entity.setApp(app.trim()); entity.setIp(ip.trim()); entity.setPort(port); // -1 is a fake value - if (null != highestSystemLoad) { - entity.setHighestSystemLoad(highestSystemLoad); - } else { - entity.setHighestSystemLoad(-1D); - } - - if (null != highestCpuUsage) { - entity.setHighestCpuUsage(highestCpuUsage); + if (avgLoad != null) { + entity.setAvgLoad(avgLoad); } else { - entity.setHighestCpuUsage(-1D); + entity.setAvgLoad(-1D); } - if (avgRt != null) { entity.setAvgRt(avgRt); } else { @@ -150,19 +144,23 @@ public Result apiAdd(String app, String ip, Integer port, try { entity = repository.save(entity); } catch (Throwable throwable) { - logger.error("Add SystemRule error", throwable); + logger.error("add error:", throwable); return Result.ofThrowable(-1, throwable); } if (!publishRules(app, ip, port)) { - logger.warn("Publish system rules fail after rule add"); + logger.info("publish system rules fail after rule add"); } return Result.ofSuccess(entity); } - @GetMapping("/save.json") - @AuthAction(PrivilegeType.WRITE_RULE) - public Result apiUpdateIfNotNull(Long id, String app, Double highestSystemLoad, - Double highestCpuUsage, Long avgRt, Long maxThread, Double qps) { + @ResponseBody + @RequestMapping("/save.json") + Result updateIfNotNull(HttpServletRequest request, Long id, String app, Double avgLoad, Long avgRt, + Long maxThread, Double qps) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -170,24 +168,15 @@ public Result apiUpdateIfNotNull(Long id, String app, Double h if (entity == null) { return Result.ofFail(-1, "id " + id + " dose not exist"); } - + authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE); if (StringUtil.isNotBlank(app)) { entity.setApp(app.trim()); } - if (highestSystemLoad != null) { - if (highestSystemLoad < 0) { - return Result.ofFail(-1, "highestSystemLoad must >= 0"); + if (avgLoad != null) { + if (avgLoad < 0) { + return Result.ofFail(-1, "avgLoad must >= 0"); } - entity.setHighestSystemLoad(highestSystemLoad); - } - if (highestCpuUsage != null) { - if (highestCpuUsage < 0) { - return Result.ofFail(-1, "highestCpuUsage must >= 0"); - } - if (highestCpuUsage > 1) { - return Result.ofFail(-1, "highestCpuUsage must <= 1"); - } - entity.setHighestCpuUsage(highestCpuUsage); + entity.setAvgLoad(avgLoad); } if (avgRt != null) { if (avgRt < 0) { @@ -221,9 +210,13 @@ public Result apiUpdateIfNotNull(Long id, String app, Double h return Result.ofSuccess(entity); } + @ResponseBody @RequestMapping("/delete.json") - @AuthAction(PrivilegeType.DELETE_RULE) - public Result delete(Long id) { + Result delete(HttpServletRequest request, Long id) { + AuthUser authUser = authService.getAuthUser(request); + if (!adminUsername.equals(authUser.getLoginName())) { + return Result.ofFail(-2, "您不是管理员,没有该权限!"); + } if (id == null) { return Result.ofFail(-1, "id can't be null"); } @@ -231,6 +224,7 @@ public Result delete(Long id) { if (oldEntity == null) { return Result.ofSuccess(null); } + authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE); try { repository.delete(id); } catch (Throwable throwable) { diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiController.java deleted file mode 100644 index c7a405d9c4..0000000000 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiController.java +++ /dev/null @@ -1,260 +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.csp.sentinel.dashboard.controller.gateway; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; -import com.alibaba.csp.sentinel.dashboard.auth.AuthService; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity; -import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; -import com.alibaba.csp.sentinel.dashboard.domain.Result; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo; -import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore; -import com.alibaba.csp.sentinel.util.StringUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.CollectionUtils; -import org.springframework.web.bind.annotation.*; - -import javax.servlet.http.HttpServletRequest; -import java.util.*; - -import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; - -/** - * Gateway api Controller for manage gateway api definitions. - * - * @author cdfive - * @since 1.7.0 - */ -@RestController -@RequestMapping(value = "/gateway/api") -public class GatewayApiController { - - private final Logger logger = LoggerFactory.getLogger(GatewayApiController.class); - - @Autowired - private InMemApiDefinitionStore repository; - - @Autowired - private SentinelApiClient sentinelApiClient; - - @GetMapping("/list.json") - @AuthAction(AuthService.PrivilegeType.READ_RULE) - public Result> queryApis(String app, String ip, Integer port) { - - if (StringUtil.isEmpty(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - if (StringUtil.isEmpty(ip)) { - return Result.ofFail(-1, "ip can't be null or empty"); - } - if (port == null) { - return Result.ofFail(-1, "port can't be null"); - } - - try { - List apis = sentinelApiClient.fetchApis(app, ip, port).get(); - repository.saveAll(apis); - return Result.ofSuccess(apis); - } catch (Throwable throwable) { - logger.error("queryApis error:", throwable); - return Result.ofThrowable(-1, throwable); - } - } - - @PostMapping("/new.json") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result addApi(HttpServletRequest request, @RequestBody AddApiReqVo reqVo) { - - String app = reqVo.getApp(); - if (StringUtil.isBlank(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - - ApiDefinitionEntity entity = new ApiDefinitionEntity(); - entity.setApp(app.trim()); - - String ip = reqVo.getIp(); - if (StringUtil.isBlank(ip)) { - return Result.ofFail(-1, "ip can't be null or empty"); - } - entity.setIp(ip.trim()); - - Integer port = reqVo.getPort(); - if (port == null) { - return Result.ofFail(-1, "port can't be null"); - } - entity.setPort(port); - - // API名称 - String apiName = reqVo.getApiName(); - if (StringUtil.isBlank(apiName)) { - return Result.ofFail(-1, "apiName can't be null or empty"); - } - entity.setApiName(apiName.trim()); - - // 匹配规则列表 - List predicateItems = reqVo.getPredicateItems(); - if (CollectionUtils.isEmpty(predicateItems)) { - return Result.ofFail(-1, "predicateItems can't empty"); - } - - List predicateItemEntities = new ArrayList<>(); - for (ApiPredicateItemVo predicateItem : predicateItems) { - ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); - - // 匹配模式 - Integer matchStrategy = predicateItem.getMatchStrategy(); - if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { - return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); - } - predicateItemEntity.setMatchStrategy(matchStrategy); - - // 匹配串 - String pattern = predicateItem.getPattern(); - if (StringUtil.isBlank(pattern)) { - return Result.ofFail(-1, "pattern can't be null or empty"); - } - predicateItemEntity.setPattern(pattern); - - predicateItemEntities.add(predicateItemEntity); - } - entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); - - // 检查API名称不能重复 - List allApis = repository.findAllByMachine(MachineInfo.of(app.trim(), ip.trim(), port)); - if (allApis.stream().map(o -> o.getApiName()).anyMatch(o -> o.equals(apiName.trim()))) { - return Result.ofFail(-1, "apiName exists: " + apiName); - } - - Date date = new Date(); - entity.setGmtCreate(date); - entity.setGmtModified(date); - - try { - entity = repository.save(entity); - } catch (Throwable throwable) { - logger.error("add gateway api error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishApis(app, ip, port)) { - logger.warn("publish gateway apis fail after add"); - } - - return Result.ofSuccess(entity); - } - - @PostMapping("/save.json") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result updateApi(@RequestBody UpdateApiReqVo reqVo) { - String app = reqVo.getApp(); - if (StringUtil.isBlank(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - - Long id = reqVo.getId(); - if (id == null) { - return Result.ofFail(-1, "id can't be null"); - } - - ApiDefinitionEntity entity = repository.findById(id); - if (entity == null) { - return Result.ofFail(-1, "api does not exist, id=" + id); - } - - // 匹配规则列表 - List predicateItems = reqVo.getPredicateItems(); - if (CollectionUtils.isEmpty(predicateItems)) { - return Result.ofFail(-1, "predicateItems can't empty"); - } - - List predicateItemEntities = new ArrayList<>(); - for (ApiPredicateItemVo predicateItem : predicateItems) { - ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity(); - - // 匹配模式 - int matchStrategy = predicateItem.getMatchStrategy(); - if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { - return Result.ofFail(-1, "Invalid matchStrategy: " + matchStrategy); - } - predicateItemEntity.setMatchStrategy(matchStrategy); - - // 匹配串 - String pattern = predicateItem.getPattern(); - if (StringUtil.isBlank(pattern)) { - return Result.ofFail(-1, "pattern can't be null or empty"); - } - predicateItemEntity.setPattern(pattern); - - predicateItemEntities.add(predicateItemEntity); - } - entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities)); - - Date date = new Date(); - entity.setGmtModified(date); - - try { - entity = repository.save(entity); - } catch (Throwable throwable) { - logger.error("update gateway api error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishApis(app, entity.getIp(), entity.getPort())) { - logger.warn("publish gateway apis fail after update"); - } - - return Result.ofSuccess(entity); - } - - @PostMapping("/delete.json") - @AuthAction(AuthService.PrivilegeType.DELETE_RULE) - - public Result deleteApi(Long id) { - if (id == null) { - return Result.ofFail(-1, "id can't be null"); - } - - ApiDefinitionEntity oldEntity = repository.findById(id); - if (oldEntity == null) { - return Result.ofSuccess(null); - } - - try { - repository.delete(id); - } catch (Throwable throwable) { - logger.error("delete gateway api error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { - logger.warn("publish gateway apis fail after delete"); - } - - return Result.ofSuccess(id); - } - - private boolean publishApis(String app, String ip, Integer port) { - List apis = repository.findAllByMachine(MachineInfo.of(app, ip, port)); - return sentinelApiClient.modifyApis(app, ip, port, apis); - } -} diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleController.java deleted file mode 100644 index 0189163781..0000000000 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleController.java +++ /dev/null @@ -1,433 +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.csp.sentinel.dashboard.controller.gateway; - - -import com.alibaba.csp.sentinel.dashboard.auth.AuthAction; -import com.alibaba.csp.sentinel.dashboard.auth.AuthService; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity; -import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo; -import com.alibaba.csp.sentinel.dashboard.domain.Result; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo; -import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore; -import com.alibaba.csp.sentinel.util.StringUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*; -import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; -import static com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity.*; - -/** - * Gateway flow rule Controller for manage gateway flow rules. - * - * @author cdfive - * @since 1.7.0 - */ -@RestController -@RequestMapping(value = "/gateway/flow") -public class GatewayFlowRuleController { - - private final Logger logger = LoggerFactory.getLogger(GatewayFlowRuleController.class); - - @Autowired - private InMemGatewayFlowRuleStore repository; - - @Autowired - private SentinelApiClient sentinelApiClient; - - @GetMapping("/list.json") - @AuthAction(AuthService.PrivilegeType.READ_RULE) - public Result> queryFlowRules(String app, String ip, Integer port) { - - if (StringUtil.isEmpty(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - if (StringUtil.isEmpty(ip)) { - return Result.ofFail(-1, "ip can't be null or empty"); - } - if (port == null) { - return Result.ofFail(-1, "port can't be null"); - } - - try { - List rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get(); - repository.saveAll(rules); - return Result.ofSuccess(rules); - } catch (Throwable throwable) { - logger.error("query gateway flow rules error:", throwable); - return Result.ofThrowable(-1, throwable); - } - } - - @PostMapping("/new.json") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result addFlowRule(@RequestBody AddFlowRuleReqVo reqVo) { - - String app = reqVo.getApp(); - if (StringUtil.isBlank(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - - GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity(); - entity.setApp(app.trim()); - - String ip = reqVo.getIp(); - if (StringUtil.isBlank(ip)) { - return Result.ofFail(-1, "ip can't be null or empty"); - } - entity.setIp(ip.trim()); - - Integer port = reqVo.getPort(); - if (port == null) { - return Result.ofFail(-1, "port can't be null"); - } - entity.setPort(port); - - // API类型, Route ID或API分组 - Integer resourceMode = reqVo.getResourceMode(); - if (resourceMode == null) { - return Result.ofFail(-1, "resourceMode can't be null"); - } - if (!Arrays.asList(RESOURCE_MODE_ROUTE_ID, RESOURCE_MODE_CUSTOM_API_NAME).contains(resourceMode)) { - return Result.ofFail(-1, "invalid resourceMode: " + resourceMode); - } - entity.setResourceMode(resourceMode); - - // API名称 - String resource = reqVo.getResource(); - if (StringUtil.isBlank(resource)) { - return Result.ofFail(-1, "resource can't be null or empty"); - } - entity.setResource(resource.trim()); - - // 针对请求属性 - GatewayParamFlowItemVo paramItem = reqVo.getParamItem(); - if (paramItem != null) { - GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); - entity.setParamItem(itemEntity); - - // 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie - Integer parseStrategy = paramItem.getParseStrategy(); - if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER - , PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { - return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy); - } - itemEntity.setParseStrategy(paramItem.getParseStrategy()); - - // 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填 - if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { - // 参数名称 - String fieldName = paramItem.getFieldName(); - if (StringUtil.isBlank(fieldName)) { - return Result.ofFail(-1, "fieldName can't be null or empty"); - } - itemEntity.setFieldName(paramItem.getFieldName()); - - String pattern = paramItem.getPattern(); - // 如果匹配串不为空,验证匹配模式 - if (StringUtil.isNotEmpty(pattern)) { - itemEntity.setPattern(pattern); - - Integer matchStrategy = paramItem.getMatchStrategy(); - if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { - return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); - } - itemEntity.setMatchStrategy(matchStrategy); - } - } - } - - // 阈值类型 0-线程数 1-QPS - Integer grade = reqVo.getGrade(); - if (grade == null) { - return Result.ofFail(-1, "grade can't be null"); - } - if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) { - return Result.ofFail(-1, "invalid grade: " + grade); - } - entity.setGrade(grade); - - // QPS阈值 - Double count = reqVo.getCount(); - if (count == null) { - return Result.ofFail(-1, "count can't be null"); - } - if (count < 0) { - return Result.ofFail(-1, "count should be at lease zero"); - } - entity.setCount(count); - - // 间隔 - Long interval = reqVo.getInterval(); - if (interval == null) { - return Result.ofFail(-1, "interval can't be null"); - } - if (interval <= 0) { - return Result.ofFail(-1, "interval should be greater than zero"); - } - entity.setInterval(interval); - - // 间隔单位 - Integer intervalUnit = reqVo.getIntervalUnit(); - if (intervalUnit == null) { - return Result.ofFail(-1, "intervalUnit can't be null"); - } - if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) { - return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit); - } - entity.setIntervalUnit(intervalUnit); - - // 流控方式 0-快速失败 2-匀速排队 - Integer controlBehavior = reqVo.getControlBehavior(); - if (controlBehavior == null) { - return Result.ofFail(-1, "controlBehavior can't be null"); - } - if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) { - return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior); - } - entity.setControlBehavior(controlBehavior); - - if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) { - // 0-快速失败, 则Burst size必填 - Integer burst = reqVo.getBurst(); - if (burst == null) { - return Result.ofFail(-1, "burst can't be null"); - } - if (burst < 0) { - return Result.ofFail(-1, "invalid burst: " + burst); - } - entity.setBurst(burst); - } else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) { - // 1-匀速排队, 则超时时间必填 - Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs(); - if (maxQueueingTimeoutMs == null) { - return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null"); - } - if (maxQueueingTimeoutMs < 0) { - return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs); - } - entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs); - } - - Date date = new Date(); - entity.setGmtCreate(date); - entity.setGmtModified(date); - - try { - entity = repository.save(entity); - } catch (Throwable throwable) { - logger.error("add gateway flow rule error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishRules(app, ip, port)) { - logger.warn("publish gateway flow rules fail after add"); - } - - return Result.ofSuccess(entity); - } - - @PostMapping("/save.json") - @AuthAction(AuthService.PrivilegeType.WRITE_RULE) - public Result updateFlowRule(@RequestBody UpdateFlowRuleReqVo reqVo) { - - String app = reqVo.getApp(); - if (StringUtil.isBlank(app)) { - return Result.ofFail(-1, "app can't be null or empty"); - } - - Long id = reqVo.getId(); - if (id == null) { - return Result.ofFail(-1, "id can't be null"); - } - - GatewayFlowRuleEntity entity = repository.findById(id); - if (entity == null) { - return Result.ofFail(-1, "gateway flow rule does not exist, id=" + id); - } - - // 针对请求属性 - GatewayParamFlowItemVo paramItem = reqVo.getParamItem(); - if (paramItem != null) { - GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); - entity.setParamItem(itemEntity); - - // 参数属性 0-ClientIP 1-Remote Host 2-Header 3-URL参数 4-Cookie - Integer parseStrategy = paramItem.getParseStrategy(); - if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER - , PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { - return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy); - } - itemEntity.setParseStrategy(paramItem.getParseStrategy()); - - // 当参数属性为2-Header 3-URL参数 4-Cookie时,参数名称必填 - if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) { - // 参数名称 - String fieldName = paramItem.getFieldName(); - if (StringUtil.isBlank(fieldName)) { - return Result.ofFail(-1, "fieldName can't be null or empty"); - } - itemEntity.setFieldName(paramItem.getFieldName()); - - String pattern = paramItem.getPattern(); - // 如果匹配串不为空,验证匹配模式 - if (StringUtil.isNotEmpty(pattern)) { - itemEntity.setPattern(pattern); - - Integer matchStrategy = paramItem.getMatchStrategy(); - if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) { - return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy); - } - itemEntity.setMatchStrategy(matchStrategy); - } - } - } else { - entity.setParamItem(null); - } - - // 阈值类型 0-线程数 1-QPS - Integer grade = reqVo.getGrade(); - if (grade == null) { - return Result.ofFail(-1, "grade can't be null"); - } - if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) { - return Result.ofFail(-1, "invalid grade: " + grade); - } - entity.setGrade(grade); - - // QPS阈值 - Double count = reqVo.getCount(); - if (count == null) { - return Result.ofFail(-1, "count can't be null"); - } - if (count < 0) { - return Result.ofFail(-1, "count should be at lease zero"); - } - entity.setCount(count); - - // 间隔 - Long interval = reqVo.getInterval(); - if (interval == null) { - return Result.ofFail(-1, "interval can't be null"); - } - if (interval <= 0) { - return Result.ofFail(-1, "interval should be greater than zero"); - } - entity.setInterval(interval); - - // 间隔单位 - Integer intervalUnit = reqVo.getIntervalUnit(); - if (intervalUnit == null) { - return Result.ofFail(-1, "intervalUnit can't be null"); - } - if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) { - return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit); - } - entity.setIntervalUnit(intervalUnit); - - // 流控方式 0-快速失败 2-匀速排队 - Integer controlBehavior = reqVo.getControlBehavior(); - if (controlBehavior == null) { - return Result.ofFail(-1, "controlBehavior can't be null"); - } - if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) { - return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior); - } - entity.setControlBehavior(controlBehavior); - - if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) { - // 0-快速失败, 则Burst size必填 - Integer burst = reqVo.getBurst(); - if (burst == null) { - return Result.ofFail(-1, "burst can't be null"); - } - if (burst < 0) { - return Result.ofFail(-1, "invalid burst: " + burst); - } - entity.setBurst(burst); - } else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) { - // 2-匀速排队, 则超时时间必填 - Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs(); - if (maxQueueingTimeoutMs == null) { - return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null"); - } - if (maxQueueingTimeoutMs < 0) { - return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs); - } - entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs); - } - - Date date = new Date(); - entity.setGmtModified(date); - - try { - entity = repository.save(entity); - } catch (Throwable throwable) { - logger.error("update gateway flow rule error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishRules(app, entity.getIp(), entity.getPort())) { - logger.warn("publish gateway flow rules fail after update"); - } - - return Result.ofSuccess(entity); - } - - - @PostMapping("/delete.json") - @AuthAction(AuthService.PrivilegeType.DELETE_RULE) - public Result deleteFlowRule(Long id) { - - if (id == null) { - return Result.ofFail(-1, "id can't be null"); - } - - GatewayFlowRuleEntity oldEntity = repository.findById(id); - if (oldEntity == null) { - return Result.ofSuccess(null); - } - - try { - repository.delete(id); - } catch (Throwable throwable) { - logger.error("delete gateway flow rule error:", throwable); - return Result.ofThrowable(-1, throwable); - } - - if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) { - logger.warn("publish gateway flow rules fail after delete"); - } - - return Result.ofSuccess(id); - } - - private boolean publishRules(String app, String ip, Integer port) { - List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port)); - return sentinelApiClient.modifyGatewayFlowRules(app, ip, port, rules); - } -} diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java index 483ebcb52f..478fe98086 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/datasource/entity/rule/SystemRuleEntity.java @@ -29,11 +29,10 @@ public class SystemRuleEntity implements RuleEntity { private String app; private String ip; private Integer port; - private Double highestSystemLoad; + private Double avgLoad; private Long avgRt; private Long maxThread; private Double qps; - private Double highestCpuUsage; private Date gmtCreate; private Date gmtModified; @@ -43,8 +42,7 @@ public static SystemRuleEntity fromSystemRule(String app, String ip, Integer por entity.setApp(app); entity.setIp(ip); entity.setPort(port); - entity.setHighestSystemLoad(rule.getHighestSystemLoad()); - entity.setHighestCpuUsage(rule.getHighestCpuUsage()); + entity.setAvgLoad(rule.getHighestSystemLoad()); entity.setAvgRt(rule.getAvgRt()); entity.setMaxThread(rule.getMaxThread()); entity.setQps(rule.getQps()); @@ -88,12 +86,12 @@ public void setApp(String app) { this.app = app; } - public Double getHighestSystemLoad() { - return highestSystemLoad; + public Double getAvgLoad() { + return avgLoad; } - public void setHighestSystemLoad(Double highestSystemLoad) { - this.highestSystemLoad = highestSystemLoad; + public void setAvgLoad(Double avgLoad) { + this.avgLoad = avgLoad; } public Long getAvgRt() { @@ -120,14 +118,6 @@ public void setQps(Double qps) { this.qps = qps; } - public Double getHighestCpuUsage() { - return highestCpuUsage; - } - - public void setHighestCpuUsage(Double highestCpuUsage) { - this.highestCpuUsage = highestCpuUsage; - } - @Override public Date getGmtCreate() { return gmtCreate; @@ -148,11 +138,10 @@ public void setGmtModified(Date gmtModified) { @Override public SystemRule toRule() { SystemRule rule = new SystemRule(); - rule.setHighestSystemLoad(highestSystemLoad); + rule.setHighestSystemLoad(avgLoad); rule.setAvgRt(avgRt); rule.setMaxThread(maxThread); rule.setQps(qps); - rule.setHighestCpuUsage(highestCpuUsage); return rule; } } diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/entity/rule/RuleEntity.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/entity/rule/RuleEntity.java new file mode 100755 index 0000000000..43c756f194 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/entity/rule/RuleEntity.java @@ -0,0 +1,39 @@ +/* + * 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.csp.sentinel.dashboard.entity.rule; + +import com.alibaba.csp.sentinel.slots.block.Rule; +import java.util.Date; + +/** + * @author leyou + */ +public interface RuleEntity { + + Long getId(); + + void setId(Long id); + + String getApp(); + + String getIp(); + + Integer getPort(); + + Date getGmtCreate(); + + Rule toRule(); +} diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java new file mode 100644 index 0000000000..1a5c5f27a3 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/filter/AuthFilter.java @@ -0,0 +1,121 @@ +/* + * 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.csp.sentinel.dashboard.filter; + +import com.alibaba.csp.sentinel.dashboard.auth.AuthService; +import java.io.IOException; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +/** + * Servlet Filter that authenticate requests. + * + * Note: Some urls are excluded as they needn't auth, such as: + * + * Index url: / Authentication request url: /login,logout Used for client: /registry/machine Static resources: + * htm,html,js and so on. + * + * The excluded urls and urlSuffixes are configured in application.properties + * + * @author cdfive + * @since 1.6.0 + */ +@Component +public class AuthFilter implements Filter { + + private static final String URL_SUFFIX_DOT = "."; + + /** + * Some urls which needn't auth, such as /auth/login,/registry/machine and so on + */ + @Value("#{'${auth.filter.exclude-urls}'.split(',')}") + private List authFilterExcludeUrls; + + /** + * Some urls with suffixes which needn't auth, such as htm,html,js and so on + */ + @Value("#{'${auth.filter.exclude-url-suffixes}'.split(',')}") + private List authFilterExcludeUrlSuffixes; + + /** + * Authentication using AuthService interface + */ + @Autowired + private AuthService authService; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + + String requestURI = httpRequest.getRequestURI(); + + // Exclude the urls which needn't auth + if (authFilterExcludeUrls.contains(requestURI)) { + chain.doFilter(request, response); + return; + } + + // Exclude the urls with suffixes which needn't auth + for (String authFilterExcludeUrlSuffix : authFilterExcludeUrlSuffixes) { + if (StringUtils.isBlank(authFilterExcludeUrlSuffix)) { + continue; + } + + // Add . for url suffix so that we needn't add . in property file + if (!authFilterExcludeUrlSuffix.startsWith(URL_SUFFIX_DOT)) { + authFilterExcludeUrlSuffix = URL_SUFFIX_DOT + authFilterExcludeUrlSuffix; + } + + if (requestURI.endsWith(authFilterExcludeUrlSuffix)) { + chain.doFilter(request, response); + return; + } + } + + AuthService.AuthUser authUser = authService.getAuthUser(httpRequest); + + HttpServletResponse httpResponse = (HttpServletResponse) response; + if (authUser == null) { + // If auth fail, set response status code to 401 + httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); + } else { + chain.doFilter(request, response); + } + } + + @Override + public void destroy() { + + } +} diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeRuleZookeeperPublisher.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeRuleZookeeperPublisher.java new file mode 100644 index 0000000000..0211f07fc1 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeRuleZookeeperPublisher.java @@ -0,0 +1,43 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.util.AssertUtil; +import java.util.List; +import org.apache.curator.framework.CuratorFramework; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.data.Stat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + + +@Component("degradeRuleZookeeperPublisher") +public class DegradeRuleZookeeperPublisher implements DynamicRulePublisher> { + + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter, String> converter; + @Autowired + private ZookeeperSentinelConfig zkConfig; + + @Override + public void publish(String app, List rules) throws Exception { + AssertUtil.notEmpty(app, "app name cannot be empty"); + + String path = zkConfig.getDegradeRulePath(app); + Stat stat = zkClient.checkExists() + .forPath(path); + if (stat == null) { + zkClient.create() + .creatingParentContainersIfNeeded() + .withMode(CreateMode.PERSISTENT) + .forPath(path, null); + } + byte[] data = CollectionUtils.isEmpty(rules) ? "[]".getBytes() : converter.convert(rules) + .getBytes(); + zkClient.setData() + .forPath(path, data); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeZookeeperProvider.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeZookeeperProvider.java new file mode 100644 index 0000000000..6c9c6a2ec2 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/DegradeZookeeperProvider.java @@ -0,0 +1,33 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import java.util.ArrayList; +import java.util.List; +import org.apache.curator.framework.CuratorFramework; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("degradeRuleZookeeperProvider") +public class DegradeZookeeperProvider implements DynamicRuleProvider> { + + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter> converter; + @Autowired + private ZookeeperSentinelConfig zkConfig; + + @Override + public List getRules(String appName) throws Exception { + String zkPath = zkConfig.getDegradeRulePath(appName); + byte[] bytes = zkClient.getData() + .forPath(zkPath); + if (null == bytes || bytes.length == 0) { + return new ArrayList<>(); + } + String s = new String(bytes); + + return converter.convert(s); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperProvider.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperProvider.java new file mode 100644 index 0000000000..cf895503c9 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperProvider.java @@ -0,0 +1,33 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import java.util.ArrayList; +import java.util.List; +import org.apache.curator.framework.CuratorFramework; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("flowRuleZookeeperProvider") +public class FlowRuleZookeeperProvider implements DynamicRuleProvider> { + + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter> converter; + @Autowired + private ZookeeperSentinelConfig zkConfig; + + @Override + public List getRules(String appName) throws Exception { + String zkPath = zkConfig.getFlowRulePath(appName); + byte[] bytes = zkClient.getData() + .forPath(zkPath); + if (null == bytes || bytes.length == 0) { + return new ArrayList<>(); + } + String s = new String(bytes); + + return converter.convert(s); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperPublisher.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperPublisher.java new file mode 100644 index 0000000000..9fbd18e44b --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/FlowRuleZookeeperPublisher.java @@ -0,0 +1,42 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.util.AssertUtil; +import java.util.List; +import org.apache.curator.framework.CuratorFramework; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.data.Stat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +@Component("flowRuleZookeeperPublisher") +public class FlowRuleZookeeperPublisher implements DynamicRulePublisher> { + + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter, String> converter; + @Autowired + private ZookeeperSentinelConfig zkConfig; + + @Override + public void publish(String app, List rules) throws Exception { + AssertUtil.notEmpty(app, "app name cannot be empty"); + + String path = zkConfig.getFlowRulePath(app); + Stat stat = zkClient.checkExists() + .forPath(path); + if (stat == null) { + zkClient.create() + .creatingParentContainersIfNeeded() + .withMode(CreateMode.PERSISTENT) + .forPath(path, null); + } + byte[] data = CollectionUtils.isEmpty(rules) ? "[]".getBytes() : converter.convert(rules) + .getBytes(); + zkClient.setData() + .forPath(path, data); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfig.java new file mode 100644 index 0000000000..605e6770e8 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfig.java @@ -0,0 +1,49 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity; +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.fastjson.JSON; +import java.util.List; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ZookeeperConfig { + + @Value("${zk.address}") + private String zkAddress; + + @Bean + public Converter, String> flowRuleEntityEncoder() { + return JSON::toJSONString; + } + + @Bean + public Converter> flowRuleEntityDecoder() { + return s -> JSON.parseArray(s, FlowRuleEntity.class); + } + + @Bean + public Converter, String> degradeRuleEntityEncoder() { + return JSON::toJSONString; + } + + @Bean + public Converter> degradeRuleEntityDecoder() { + return s -> JSON.parseArray(s, DegradeRuleEntity.class); + } + + @Bean + public CuratorFramework zkClient() { + CuratorFramework zkClient = CuratorFrameworkFactory.newClient(zkAddress, + new ExponentialBackoffRetry(ZookeeperConfigUtil.SLEEP_TIME, ZookeeperConfigUtil.RETRY_TIMES)); + zkClient.start(); + + return zkClient; + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfigUtil.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfigUtil.java new file mode 100644 index 0000000000..d15811e5b0 --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperConfigUtil.java @@ -0,0 +1,26 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import org.apache.commons.lang.StringUtils; + +public class ZookeeperConfigUtil { + + public static final String RULE_ROOT_PATH = "/sentinel_rules"; + + public static final int RETRY_TIMES = 3; + public static final int SLEEP_TIME = 1000; + + public static String getPath(String appName) { + StringBuilder stringBuilder = new StringBuilder(RULE_ROOT_PATH); + + if (StringUtils.isBlank(appName)) { + return stringBuilder.toString(); + } + if (appName.startsWith("/")) { + stringBuilder.append(appName); + } else { + stringBuilder.append("/") + .append(appName); + } + return stringBuilder.toString(); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperSentinelConfig.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperSentinelConfig.java new file mode 100644 index 0000000000..1ac85c7cda --- /dev/null +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/ZookeeperSentinelConfig.java @@ -0,0 +1,76 @@ +package com.alibaba.csp.sentinel.dashboard.rule; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.datasource.ReadableDataSource; +import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import java.util.List; +import javax.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class ZookeeperSentinelConfig { + + private final Logger logger = LoggerFactory.getLogger(ZookeeperSentinelConfig.class); + private final String FLOW_PATH = "/flow"; + private final String DEGRADE_PATH = "/degrade"; + @Value("${zk.address}") + private String zkAddress; + @Value("${zk.sentinel.path}") + private String zkPath; + @Value("${zk.sentinel.appName}") + private String appName; + + @PostConstruct + public void loadRules() { + //Flow + ReadableDataSource> flowRuleDataSource = new ZookeeperDataSource<>(zkAddress, + zkPath + appName, source -> JSON.parseObject(source, new TypeReference>() { + })); + FlowRuleManager.register2Property(flowRuleDataSource.getProperty()); + + //Degrade + String degradePath = zkPath + appName + DEGRADE_PATH; + Converter> degradeRules = source -> JSON.parseObject(source, + new TypeReference>() { + }); + ReadableDataSource> zkDataSourceDegrade = new ZookeeperDataSource<>(zkAddress, + degradePath, degradeRules); + DegradeRuleManager.register2Property(zkDataSourceDegrade.getProperty()); + logger.info("----------------- Sentinel DataSource Zookeeper Init Success -------------------"); + } + + + public String getFlowRulePath(String appName) { + if (appName.startsWith("/")) { + return zkPath + appName + FLOW_PATH; + } else { + return zkPath + "/" + appName + FLOW_PATH; + } + } + + public String getDegradeRulePath(String appName) { + if (appName.startsWith("/")) { + return zkPath + appName + DEGRADE_PATH; + } else { + return zkPath + "/" + appName + DEGRADE_PATH; + } + } + + public String getZkAddress() { + return zkAddress; + } + + public String getZkPath() { + return zkPath; + } + +} diff --git a/sentinel-dashboard/src/main/resources/application-stg.properties b/sentinel-dashboard/src/main/resources/application-stg.properties new file mode 100755 index 0000000000..9a1c028224 --- /dev/null +++ b/sentinel-dashboard/src/main/resources/application-stg.properties @@ -0,0 +1,24 @@ +#spring settings +spring.http.encoding.force=true +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true + +#logging settings +logging.level.org.springframework.web=INFO +logging.file=${user.home}/logs/csp/sentinel-dashboard.log +logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n +#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + +#auth settings +auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine,/version +auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png +auth.username=sentinel +auth.password=sentinel +auth.admin.username=admin +auth.admin.password=admin +# Inject the dashboard version. It's required to enable +# filtering in pom.xml for this resource file. +sentinel.dashboard.version=${project.version} +zk.address=stg-dwhxenial001-stg-cloud008.phonepe.nm2:2181 +zk.sentinel.path=/sentinel_rules +zk.sentinel.appName=/sentinel-dashboard \ No newline at end of file diff --git a/sentinel-dashboard/src/main/resources/application-test.properties b/sentinel-dashboard/src/main/resources/application-test.properties new file mode 100755 index 0000000000..a089db3634 --- /dev/null +++ b/sentinel-dashboard/src/main/resources/application-test.properties @@ -0,0 +1,13 @@ +#spring settings +spring.http.encoding.force=true +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +#logging settings +logging.level.org.springframework.web=INFO +logging.file=/tmp/logs/sentinel-dashboard.log +logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n +#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n +#Zookeeper +zk.address=localhost:2181 +zk.sentinel.path=/sentinel_rules +zk.sentinel.appName=/sentinel-dashboard \ No newline at end of file diff --git a/sentinel-dashboard/src/main/resources/application.properties b/sentinel-dashboard/src/main/resources/application.properties index a2f84dec89..9a1c028224 100755 --- a/sentinel-dashboard/src/main/resources/application.properties +++ b/sentinel-dashboard/src/main/resources/application.properties @@ -12,10 +12,13 @@ logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - % #auth settings auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine,/version auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png -# If auth.enabled=false, Sentinel console disable login auth.username=sentinel auth.password=sentinel - +auth.admin.username=admin +auth.admin.password=admin # Inject the dashboard version. It's required to enable # filtering in pom.xml for this resource file. -sentinel.dashboard.version=${project.version} \ No newline at end of file +sentinel.dashboard.version=${project.version} +zk.address=stg-dwhxenial001-stg-cloud008.phonepe.nm2:2181 +zk.sentinel.path=/sentinel_rules +zk.sentinel.appName=/sentinel-dashboard \ No newline at end of file diff --git a/sentinel-dashboard/src/main/resources/application.yml b/sentinel-dashboard/src/main/resources/application.yml new file mode 100644 index 0000000000..360bd95c96 --- /dev/null +++ b/sentinel-dashboard/src/main/resources/application.yml @@ -0,0 +1,7 @@ +my: + name: sentinel + +spring: + profiles: + active: test + \ No newline at end of file diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java deleted file mode 100644 index 9751c8311e..0000000000 --- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayApiControllerTest.java +++ /dev/null @@ -1,328 +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.csp.sentinel.dashboard.controller.gateway; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor; -import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiPredicateItemEntity; -import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; -import com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery; -import com.alibaba.csp.sentinel.dashboard.domain.Result; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.AddApiReqVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.ApiPredicateItemVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.api.UpdateApiReqVo; -import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemApiDefinitionStore; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.TypeReference; -import org.apache.commons.lang3.time.DateUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; - -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; - -/** - * Test cases for {@link GatewayApiController}. - * - * @author cdfive - */ -@RunWith(SpringRunner.class) -@WebMvcTest(GatewayApiController.class) -@Import({FakeAuthServiceImpl.class, InMemApiDefinitionStore.class, AppManagement.class, SimpleMachineDiscovery.class, AuthorizationInterceptor.class}) -public class GatewayApiControllerTest { - - private static final String TEST_APP = "test_app"; - - private static final String TEST_IP = "localhost"; - - private static final Integer TEST_PORT = 8719; - - @Autowired - private MockMvc mockMvc; - - @Autowired - private InMemApiDefinitionStore repository; - - @MockBean - private SentinelApiClient sentinelApiClient; - - @Before - public void before() { - repository.clearAll(); - } - - @Test - public void testQueryApis() throws Exception { - String path = "/gateway/api/list.json"; - - List entities = new ArrayList<>(); - - // Mock two entities - ApiDefinitionEntity entity = new ApiDefinitionEntity(); - entity.setId(1L); - entity.setApp(TEST_APP); - entity.setIp(TEST_IP); - entity.setPort(TEST_PORT); - entity.setApiName("foo"); - Date date = new Date(); - entity.setGmtCreate(date); - entity.setGmtModified(date); - - Set itemEntities = new LinkedHashSet<>(); - entity.setPredicateItems(itemEntities); - ApiPredicateItemEntity itemEntity = new ApiPredicateItemEntity(); - itemEntity.setPattern("/aaa"); - itemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); - - itemEntities.add(itemEntity); - entities.add(entity); - - ApiDefinitionEntity entity2 = new ApiDefinitionEntity(); - entity2.setId(2L); - entity2.setApp(TEST_APP); - entity2.setIp(TEST_IP); - entity2.setPort(TEST_PORT); - entity2.setApiName("biz"); - entity.setGmtCreate(date); - entity.setGmtModified(date); - - Set itemEntities2 = new LinkedHashSet<>(); - entity2.setPredicateItems(itemEntities2); - ApiPredicateItemEntity itemEntity2 = new ApiPredicateItemEntity(); - itemEntity2.setPattern("/bbb"); - itemEntity2.setMatchStrategy(URL_MATCH_STRATEGY_PREFIX); - - itemEntities2.add(itemEntity2); - entities.add(entity2); - - CompletableFuture> completableFuture = mock(CompletableFuture.class); - given(completableFuture.get()).willReturn(entities); - given(sentinelApiClient.fetchApis(TEST_APP, TEST_IP, TEST_PORT)).willReturn(completableFuture); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path); - requestBuilder.param("app", TEST_APP); - requestBuilder.param("ip", TEST_IP); - requestBuilder.param("port", String.valueOf(TEST_PORT)); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the fetchApis method has been called - verify(sentinelApiClient).fetchApis(TEST_APP, TEST_IP, TEST_PORT); - - // Verify if two same entities are got - Result> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>>(){}); - assertTrue(result.isSuccess()); - - List data = result.getData(); - assertEquals(2, data.size()); - assertEquals(entities, data); - - // Verify the entities are add into memory repository - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(2, entitiesInMem.size()); - assertEquals(entities, entitiesInMem); - } - - @Test - public void testAddApi() throws Exception { - String path = "/gateway/api/new.json"; - - AddApiReqVo reqVo = new AddApiReqVo(); - reqVo.setApp(TEST_APP); - reqVo.setIp(TEST_IP); - reqVo.setPort(TEST_PORT); - - reqVo.setApiName("customized_api"); - - List itemVos = new ArrayList<>(); - ApiPredicateItemVo itemVo = new ApiPredicateItemVo(); - itemVo.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); - itemVo.setPattern("/product"); - itemVos.add(itemVo); - reqVo.setPredicateItems(itemVos); - - given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyApis method has been called - verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() {}); - assertTrue(result.isSuccess()); - - // Verify the result - ApiDefinitionEntity entity = result.getData(); - assertNotNull(entity); - assertEquals(TEST_APP, entity.getApp()); - assertEquals(TEST_IP, entity.getIp()); - assertEquals(TEST_PORT, entity.getPort()); - assertEquals("customized_api", entity.getApiName()); - assertNotNull(entity.getId()); - assertNotNull(entity.getGmtCreate()); - assertNotNull(entity.getGmtModified()); - - Set predicateItemEntities = entity.getPredicateItems(); - assertEquals(1, predicateItemEntities.size()); - ApiPredicateItemEntity predicateItemEntity = predicateItemEntities.iterator().next(); - assertEquals(URL_MATCH_STRATEGY_EXACT, predicateItemEntity.getMatchStrategy().intValue()); - assertEquals("/product", predicateItemEntity.getPattern()); - - // Verify the entity which is add in memory repository - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(1, entitiesInMem.size()); - assertEquals(entity, entitiesInMem.get(0)); - } - - @Test - public void testUpdateApi() throws Exception { - String path = "/gateway/api/save.json"; - - // Add one entity to memory repository for update - ApiDefinitionEntity addEntity = new ApiDefinitionEntity(); - addEntity.setApp(TEST_APP); - addEntity.setIp(TEST_IP); - addEntity.setPort(TEST_PORT); - addEntity.setApiName("bbb"); - Date date = new Date(); - // To make the gmtModified different when do update - date = DateUtils.addSeconds(date, -1); - addEntity.setGmtCreate(date); - addEntity.setGmtModified(date); - Set addRedicateItemEntities = new HashSet<>(); - addEntity.setPredicateItems(addRedicateItemEntities); - ApiPredicateItemEntity addPredicateItemEntity = new ApiPredicateItemEntity(); - addPredicateItemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); - addPredicateItemEntity.setPattern("/order"); - addEntity = repository.save(addEntity); - - UpdateApiReqVo reqVo = new UpdateApiReqVo(); - reqVo.setId(addEntity.getId()); - reqVo.setApp(TEST_APP); - List itemVos = new ArrayList<>(); - ApiPredicateItemVo itemVo = new ApiPredicateItemVo(); - itemVo.setMatchStrategy(URL_MATCH_STRATEGY_PREFIX); - itemVo.setPattern("/my_order"); - itemVos.add(itemVo); - reqVo.setPredicateItems(itemVos); - - given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyApis method has been called - verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() {}); - assertTrue(result.isSuccess()); - - ApiDefinitionEntity entity = result.getData(); - assertNotNull(entity); - assertEquals("bbb", entity.getApiName()); - assertEquals(date, entity.getGmtCreate()); - // To make sure gmtModified has been set and it's different from gmtCreate - assertNotNull(entity.getGmtModified()); - assertNotEquals(entity.getGmtCreate(), entity.getGmtModified()); - - Set predicateItemEntities = entity.getPredicateItems(); - assertEquals(1, predicateItemEntities.size()); - ApiPredicateItemEntity predicateItemEntity = predicateItemEntities.iterator().next(); - assertEquals(URL_MATCH_STRATEGY_PREFIX, predicateItemEntity.getMatchStrategy().intValue()); - assertEquals("/my_order", predicateItemEntity.getPattern()); - - // Verify the entity which is update in memory repository - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(1, entitiesInMem.size()); - assertEquals(entity, entitiesInMem.get(0)); - } - - @Test - public void testDeleteApi() throws Exception { - String path = "/gateway/api/delete.json"; - - // Add one entity into memory repository for delete - ApiDefinitionEntity addEntity = new ApiDefinitionEntity(); - addEntity.setApp(TEST_APP); - addEntity.setIp(TEST_IP); - addEntity.setPort(TEST_PORT); - addEntity.setApiName("ccc"); - Date date = new Date(); - addEntity.setGmtCreate(date); - addEntity.setGmtModified(date); - Set addRedicateItemEntities = new HashSet<>(); - addEntity.setPredicateItems(addRedicateItemEntities); - ApiPredicateItemEntity addPredicateItemEntity = new ApiPredicateItemEntity(); - addPredicateItemEntity.setMatchStrategy(URL_MATCH_STRATEGY_EXACT); - addPredicateItemEntity.setPattern("/user/add"); - addEntity = repository.save(addEntity); - - given(sentinelApiClient.modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.param("id", String.valueOf(addEntity.getId())); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyApis method has been called - verify(sentinelApiClient).modifyApis(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - // Verify the result - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() {}); - assertTrue(result.isSuccess()); - - assertEquals(addEntity.getId(), result.getData()); - - // Now no entities in memory - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(0, entitiesInMem.size()); - } -} diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java deleted file mode 100644 index b9c2353e47..0000000000 --- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/controller/gateway/GatewayFlowRuleControllerTest.java +++ /dev/null @@ -1,357 +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.csp.sentinel.dashboard.controller.gateway; - -import com.alibaba.csp.sentinel.dashboard.auth.AuthorizationInterceptor; -import com.alibaba.csp.sentinel.dashboard.auth.FakeAuthServiceImpl; -import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity; -import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity; -import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement; -import com.alibaba.csp.sentinel.dashboard.discovery.SimpleMachineDiscovery; -import com.alibaba.csp.sentinel.dashboard.domain.Result; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo; -import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo; -import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.TypeReference; -import org.apache.commons.lang3.time.DateUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*; -import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; - -/** - * Test cases for {@link GatewayFlowRuleController}. - * - * @author cdfive - */ -@RunWith(SpringRunner.class) -@WebMvcTest(GatewayFlowRuleController.class) -@Import({FakeAuthServiceImpl.class, InMemGatewayFlowRuleStore.class, AppManagement.class, SimpleMachineDiscovery.class, - AuthorizationInterceptor.class }) -public class GatewayFlowRuleControllerTest { - - private static final String TEST_APP = "test_app"; - - private static final String TEST_IP = "localhost"; - - private static final Integer TEST_PORT = 8719; - - @Autowired - private MockMvc mockMvc; - - @Autowired - private InMemGatewayFlowRuleStore repository; - - @MockBean - private SentinelApiClient sentinelApiClient; - - @Before - public void before() { - repository.clearAll(); - } - - @Test - public void testQueryFlowRules() throws Exception { - String path = "/gateway/flow/list.json"; - - List entities = new ArrayList<>(); - - // Mock two entities - GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity(); - entity.setId(1L); - entity.setApp(TEST_APP); - entity.setIp(TEST_IP); - entity.setPort(TEST_PORT); - entity.setResource("httpbin_route"); - entity.setResourceMode(RESOURCE_MODE_ROUTE_ID); - entity.setGrade(FLOW_GRADE_QPS); - entity.setCount(5D); - entity.setInterval(30L); - entity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); - entity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); - entity.setBurst(0); - entity.setMaxQueueingTimeoutMs(0); - - GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity(); - entity.setParamItem(itemEntity); - itemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); - entities.add(entity); - - GatewayFlowRuleEntity entity2 = new GatewayFlowRuleEntity(); - entity2.setId(2L); - entity2.setApp(TEST_APP); - entity2.setIp(TEST_IP); - entity2.setPort(TEST_PORT); - entity2.setResource("some_customized_api"); - entity2.setResourceMode(RESOURCE_MODE_CUSTOM_API_NAME); - entity2.setCount(30D); - entity2.setInterval(2L); - entity2.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE); - entity2.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); - entity2.setBurst(0); - entity2.setMaxQueueingTimeoutMs(0); - - GatewayParamFlowItemEntity itemEntity2 = new GatewayParamFlowItemEntity(); - entity2.setParamItem(itemEntity2); - itemEntity2.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); - entities.add(entity2); - - CompletableFuture> completableFuture = mock(CompletableFuture.class); - given(completableFuture.get()).willReturn(entities); - given(sentinelApiClient.fetchGatewayFlowRules(TEST_APP, TEST_IP, TEST_PORT)).willReturn(completableFuture); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(path); - requestBuilder.param("app", TEST_APP); - requestBuilder.param("ip", TEST_IP); - requestBuilder.param("port", String.valueOf(TEST_PORT)); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the fetchGatewayFlowRules method has been called - verify(sentinelApiClient).fetchGatewayFlowRules(TEST_APP, TEST_IP, TEST_PORT); - - // Verify if two same entities are got - Result> result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>>(){}); - assertTrue(result.isSuccess()); - - List data = result.getData(); - assertEquals(2, data.size()); - assertEquals(entities, data); - - // Verify the entities are add into memory repository - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(2, entitiesInMem.size()); - assertEquals(entities, entitiesInMem); - } - - @Test - public void testAddFlowRule() throws Exception { - String path = "/gateway/flow/new.json"; - - AddFlowRuleReqVo reqVo = new AddFlowRuleReqVo(); - reqVo.setApp(TEST_APP); - reqVo.setIp(TEST_IP); - reqVo.setPort(TEST_PORT); - - reqVo.setResourceMode(RESOURCE_MODE_ROUTE_ID); - reqVo.setResource("httpbin_route"); - - reqVo.setGrade(FLOW_GRADE_QPS); - reqVo.setCount(5D); - reqVo.setInterval(30L); - reqVo.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); - reqVo.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); - reqVo.setBurst(0); - reqVo.setMaxQueueingTimeoutMs(0); - - given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyGatewayFlowRules method has been called - verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() {}); - assertTrue(result.isSuccess()); - - // Verify the result - GatewayFlowRuleEntity entity = result.getData(); - assertNotNull(entity); - assertEquals(TEST_APP, entity.getApp()); - assertEquals(TEST_IP, entity.getIp()); - assertEquals(TEST_PORT, entity.getPort()); - assertEquals(RESOURCE_MODE_ROUTE_ID, entity.getResourceMode().intValue()); - assertEquals("httpbin_route", entity.getResource()); - assertNotNull(entity.getId()); - assertNotNull(entity.getGmtCreate()); - assertNotNull(entity.getGmtModified()); - - // Verify the entity which is add in memory repository - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(1, entitiesInMem.size()); - assertEquals(entity, entitiesInMem.get(0)); - } - - @Test - public void testUpdateFlowRule() throws Exception { - String path = "/gateway/flow/save.json"; - - // Add one entity into memory repository for update - GatewayFlowRuleEntity addEntity = new GatewayFlowRuleEntity(); - addEntity.setId(1L); - addEntity.setApp(TEST_APP); - addEntity.setIp(TEST_IP); - addEntity.setPort(TEST_PORT); - addEntity.setResource("httpbin_route"); - addEntity.setResourceMode(RESOURCE_MODE_ROUTE_ID); - addEntity.setGrade(FLOW_GRADE_QPS); - addEntity.setCount(5D); - addEntity.setInterval(30L); - addEntity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); - addEntity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); - addEntity.setBurst(0); - addEntity.setMaxQueueingTimeoutMs(0); - Date date = new Date(); - // To make the gmtModified different when do update - date = DateUtils.addSeconds(date, -1); - addEntity.setGmtCreate(date); - addEntity.setGmtModified(date); - - GatewayParamFlowItemEntity addItemEntity = new GatewayParamFlowItemEntity(); - addEntity.setParamItem(addItemEntity); - addItemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); - - repository.save(addEntity); - - UpdateFlowRuleReqVo reqVo = new UpdateFlowRuleReqVo(); - reqVo.setId(addEntity.getId()); - reqVo.setApp(TEST_APP); - reqVo.setGrade(FLOW_GRADE_QPS); - reqVo.setCount(6D); - reqVo.setInterval(2L); - reqVo.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE); - reqVo.setControlBehavior(CONTROL_BEHAVIOR_RATE_LIMITER); - reqVo.setMaxQueueingTimeoutMs(500); - - GatewayParamFlowItemVo itemVo = new GatewayParamFlowItemVo(); - reqVo.setParamItem(itemVo); - itemVo.setParseStrategy(PARAM_PARSE_STRATEGY_URL_PARAM); - itemVo.setFieldName("pa"); - - given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.content(JSON.toJSONString(reqVo)).contentType(MediaType.APPLICATION_JSON); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyGatewayFlowRules method has been called - verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() { - }); - assertTrue(result.isSuccess()); - - GatewayFlowRuleEntity entity = result.getData(); - assertNotNull(entity); - assertEquals(RESOURCE_MODE_ROUTE_ID, entity.getResourceMode().intValue()); - assertEquals("httpbin_route", entity.getResource()); - assertEquals(6D, entity.getCount().doubleValue(), 0); - assertEquals(2L, entity.getInterval().longValue()); - assertEquals(GatewayFlowRuleEntity.INTERVAL_UNIT_MINUTE, entity.getIntervalUnit().intValue()); - assertEquals(CONTROL_BEHAVIOR_RATE_LIMITER, entity.getControlBehavior().intValue()); - assertEquals(0, entity.getBurst().intValue()); - assertEquals(500, entity.getMaxQueueingTimeoutMs().intValue()); - assertEquals(date, entity.getGmtCreate()); - // To make sure gmtModified has been set and it's different from gmtCreate - assertNotNull(entity.getGmtModified()); - assertNotEquals(entity.getGmtCreate(), entity.getGmtModified()); - - // Verify the entity which is update in memory repository - GatewayParamFlowItemEntity itemEntity = entity.getParamItem(); - assertEquals(PARAM_PARSE_STRATEGY_URL_PARAM, itemEntity.getParseStrategy().intValue()); - assertEquals("pa", itemEntity.getFieldName()); - } - - @Test - public void testDeleteFlowRule() throws Exception { - String path = "/gateway/flow/delete.json"; - - // Add one entity into memory repository for delete - GatewayFlowRuleEntity addEntity = new GatewayFlowRuleEntity(); - addEntity.setId(1L); - addEntity.setApp(TEST_APP); - addEntity.setIp(TEST_IP); - addEntity.setPort(TEST_PORT); - addEntity.setResource("httpbin_route"); - addEntity.setResourceMode(RESOURCE_MODE_ROUTE_ID); - addEntity.setGrade(FLOW_GRADE_QPS); - addEntity.setCount(5D); - addEntity.setInterval(30L); - addEntity.setIntervalUnit(GatewayFlowRuleEntity.INTERVAL_UNIT_SECOND); - addEntity.setControlBehavior(CONTROL_BEHAVIOR_DEFAULT); - addEntity.setBurst(0); - addEntity.setMaxQueueingTimeoutMs(0); - Date date = new Date(); - date = DateUtils.addSeconds(date, -1); - addEntity.setGmtCreate(date); - addEntity.setGmtModified(date); - - GatewayParamFlowItemEntity addItemEntity = new GatewayParamFlowItemEntity(); - addEntity.setParamItem(addItemEntity); - addItemEntity.setParseStrategy(PARAM_PARSE_STRATEGY_CLIENT_IP); - - repository.save(addEntity); - - given(sentinelApiClient.modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any())).willReturn(true); - - MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(path); - requestBuilder.param("id", String.valueOf(addEntity.getId())); - - // Do controller logic - MvcResult mvcResult = mockMvc.perform(requestBuilder) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn(); - - // Verify the modifyGatewayFlowRules method has been called - verify(sentinelApiClient).modifyGatewayFlowRules(eq(TEST_APP), eq(TEST_IP), eq(TEST_PORT), any()); - - // Verify the result - Result result = JSONObject.parseObject(mvcResult.getResponse().getContentAsString(), new TypeReference>() {}); - assertTrue(result.isSuccess()); - - assertEquals(addEntity.getId(), result.getData()); - - // Now no entities in memory - List entitiesInMem = repository.findAllByApp(TEST_APP); - assertEquals(0, entitiesInMem.size()); - } -}