Skip to content

Commit

Permalink
Add support for authority rule configuration in Sentinel Dashboard (a…
Browse files Browse the repository at this point in the history
…libaba#189)

- Update dashboard API client to support authority rule commands
- Add REST API and frontend page for authority rules

Signed-off-by: Eric Zhao <[email protected]>
  • Loading branch information
sczyh30 authored Oct 23, 2018
1 parent 3982d70 commit 649a44d
Show file tree
Hide file tree
Showing 12 changed files with 648 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,27 @@ public boolean setSystemRuleOfMachine(String app, String ip, int port, List<Syst
return true;
}

public boolean setAuthorityRuleOfMachine(String app, String ip, int port, List<AuthorityRuleEntity> rules) {
if (rules == null) {
return true;
}
if (StringUtil.isBlank(ip) || port <= 0) {
throw new IllegalArgumentException("Invalid IP or port");
}
String data = JSON.toJSONString(
rules.stream().map(AuthorityRuleEntity::getRule).collect(Collectors.toList()));
try {
data = URLEncoder.encode(data, DEFAULT_CHARSET.name());
} catch (UnsupportedEncodingException e) {
logger.info("Encode rule error", e);
return false;
}
String url = "http://" + ip + ":" + port + "/" + SET_RULES_PATH + "?type=" + AUTHORITY_TYPE + "&data=" + data;
String result = httpGetContent(url);
logger.info("Push authority rules: " + result);
return true;
}

public CompletableFuture<Void> setParamFlowRuleOfMachine(String app, String ip, int port, List<ParamFlowRuleEntity> rules) {
if (rules == null) {
return CompletableFuture.completedFuture(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.util.AssertUtil;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
* @author Eric Zhao
* @since 0.2.1
*/
public class AuthorityRuleEntity extends AbstractRuleEntity<AuthorityRule> {

public AuthorityRuleEntity() {}

public AuthorityRuleEntity(AuthorityRule authorityRule) {
AssertUtil.notNull(authorityRule, "Authority rule should not be null");
this.rule = authorityRule;
Expand All @@ -37,14 +41,17 @@ public static AuthorityRuleEntity fromAuthorityRule(String app, String ip, Integ
return entity;
}

@JsonIgnore
public String getLimitApp() {
return rule.getLimitApp();
}

@JsonIgnore
public String getResource() {
return rule.getResource();
}

@JsonIgnore
public int getStrategy() {
return rule.getStrategy();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.taobao.csp.sentinel.dashboard.repository.rule;

import java.util.concurrent.atomic.AtomicLong;

import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import org.springframework.stereotype.Component;

/**
* In-memory storage for authority rules.
*
* @author Eric Zhao
* @since 0.2.1
*/
@Component
public class InMemAuthorityRuleStore extends InMemoryRuleRepositoryAdapter<AuthorityRuleEntity> {

private static AtomicLong ids = new AtomicLong(0);

@Override
protected long nextId() {
return ids.incrementAndGet();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,27 @@
*/
package com.taobao.csp.sentinel.dashboard.view;

import java.util.Date;
import java.util.List;

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;

import com.taobao.csp.sentinel.dashboard.client.SentinelApiClient;
import com.taobao.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
import com.taobao.csp.sentinel.dashboard.repository.rule.RuleRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
Expand All @@ -34,4 +50,134 @@ public class AuthorityRuleController {

@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
private RuleRepository<AuthorityRuleEntity, Long> repository;

@GetMapping("/rules")
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip cannot be null or empty");
}
if (port == null || port <= 0) {
return Result.ofFail(-1, "Invalid parameter: port");
}
try {
List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying authority rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}

private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {
if (entity == null) {
return Result.ofFail(-1, "bad rule body");
}
if (StringUtil.isBlank(entity.getApp())) {
return Result.ofFail(-1, "app can't be null or empty");
}
if (StringUtil.isBlank(entity.getIp())) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (entity.getPort() == null || entity.getPort() <= 0) {
return Result.ofFail(-1, "port can't be null");
}
if (entity.getRule() == null) {
return Result.ofFail(-1, "rule can't be null");
}
if (StringUtil.isBlank(entity.getResource())) {
return Result.ofFail(-1, "resource name cannot be null or empty");
}
if (StringUtil.isBlank(entity.getLimitApp())) {
return Result.ofFail(-1, "limitApp should be valid");
}
if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE
&& entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) {
return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)");
}
return null;
}

@PostMapping("/rule")
public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) {
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setId(null);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
} catch (Throwable throwable) {
logger.error("Failed to add authority rule", throwable);
return Result.ofThrowable(-1, throwable);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.info("Publish authority rules failed after rule add");
}
return Result.ofSuccess(entity);
}

@PutMapping("/rule/{id}")
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody AuthorityRuleEntity entity) {
if (id == null || id <= 0) {
return Result.ofFail(-1, "Invalid id");
}
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setId(id);
Date date = new Date();
entity.setGmtCreate(null);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
if (entity == null) {
return Result.ofFail(-1, "Failed to save authority rule");
}
} catch (Throwable throwable) {
logger.error("Failed to save authority rule", throwable);
return Result.ofThrowable(-1, throwable);
}
if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
logger.info("Publish authority rules failed after rule update");
}
return Result.ofSuccess(entity);
}

@DeleteMapping("/rule/{id}")
public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
if (id == null) {
return Result.ofFail(-1, "id cannot be null");
}
AuthorityRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
try {
repository.delete(id);
} catch (Exception e) {
return Result.ofFail(-1, e.getMessage());
}
if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
logger.error("Publish authority rules failed after rule delete");
}
return Result.ofSuccess(id);
}

private boolean publishRules(String app, String ip, Integer port) {
List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
}
}
16 changes: 16 additions & 0 deletions sentinel-dashboard/src/main/webapp/resources/app/scripts/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ angular
}
})

.state('dashboard.authority', {
templateUrl: 'app/views/authority.html',
url: '/authority/:app',
controller: 'AuthorityRuleController',
resolve: {
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'sentinelDashboardApp',
files: [
'app/scripts/controllers/authority.js',
]
});
}]
}
})

.state('dashboard.degrade', {
templateUrl: 'app/views/degrade.html',
url: '/degrade/:app',
Expand Down
Loading

0 comments on commit 649a44d

Please sign in to comment.