Merge microservices governance to main branch (#3066)
* feature:microservices governance modulespull/3079/head
parent
19a477a13a
commit
e0636ecfb3
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
Binary file not shown.
After Width: | Height: | Size: 165 KiB |
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>istio-authentication-provider-mvc-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Authentication Provider MVC Example</name>
|
||||
<description>Example demonstrating how to use authentication on spring mvc application</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class AuthApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class AuthMvcController {
|
||||
|
||||
@RequestMapping("/auth")
|
||||
public String auth(HttpServletRequest request) {
|
||||
String resp = "received request from " + request.getRemoteHost()
|
||||
+ ", local addr is " + request.getLocalAddr() + ", local host is "
|
||||
+ request.getLocalName() + ", request path is" + request.getRequestURI();
|
||||
return resp;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:80}
|
||||
spring:
|
||||
cloud:
|
||||
governance:
|
||||
auth:
|
||||
enabled: ${ISTIO_AUTH_ENABLE:true}
|
||||
istio:
|
||||
config:
|
||||
enabled: ${ISTIO_CONFIG_ENABLE:true}
|
||||
host: ${ISTIOD_ADDR:127.0.0.1}
|
||||
port: ${ISTIOD_PORT:15010}
|
||||
polling-pool-size: ${POLLING_POOL_SIZE:10}
|
||||
polling-time: ${POLLING_TIME:10}
|
||||
istiod-token: ${ISTIOD_TOKEN:}
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>istio-authentication-provider-webflux-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Authentication Provider WebFlux Example</name>
|
||||
<description>Example demonstrating how to use authentication on spring webflux application</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>spring-web</groupId>
|
||||
<artifactId>5.2.15.RELEASE</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>spring-web</groupId>
|
||||
<artifactId>5.2.15.RELEASE</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
@RestController
|
||||
public class AuthWebFluxController {
|
||||
|
||||
@RequestMapping("/auth")
|
||||
public Mono<String> auth(ServerWebExchange request) {
|
||||
String resp = "received request from "
|
||||
+ request.getRequest().getRemoteAddress().getAddress().getHostAddress()
|
||||
+ ", local addr is "
|
||||
+ request.getRequest().getLocalAddress().getAddress().getHostAddress()
|
||||
+ ", local host is "
|
||||
+ request.getRequest().getLocalAddress().getAddress().getHostName()
|
||||
+ ", request path is" + request.getRequest().getURI().getPath();
|
||||
return Mono.just(resp);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ScaAuthReactiveApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ScaAuthReactiveApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:80}
|
||||
spring:
|
||||
cloud:
|
||||
governance:
|
||||
auth:
|
||||
enabled: ${ISTIO_AUTH_ENABLE:true}
|
||||
istio:
|
||||
config:
|
||||
enabled: ${ISTIO_CONFIG_ENABLE:true}
|
||||
host: ${ISTIOD_ADDR:127.0.0.1}
|
||||
port: ${ISTIOD_PORT:15010}
|
||||
polling-pool-size: ${POLLING_POOL_SIZE:10}
|
||||
polling-time: ${POLLING_TIME:10}
|
||||
istiod-token: ${ISTIOD_TOKEN:}
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>consumer-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Label Routing Consumer Example</name>
|
||||
<description>Example demonstrating how to use label routing consumer</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient(autoRegister = true)
|
||||
@EnableFeignClients
|
||||
public class ConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
@FeignClient(name = "service-provider")
|
||||
public interface FeignService {
|
||||
|
||||
/**
|
||||
* Feign test.
|
||||
* @return String
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
String test();
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class Controller implements ApplicationContextAware {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private ConsumerApplication.FeignService feignService;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@GetMapping("/router-test")
|
||||
public String notFound() {
|
||||
return feignService.test();
|
||||
}
|
||||
|
||||
@GetMapping("/add")
|
||||
public void getDataFromControlPlaneTest() {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
|
||||
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
|
||||
unifiedRouteDataStructure.setTargetService("service-provider");
|
||||
|
||||
LabelRouteRule labelRouteData = new LabelRouteRule();
|
||||
labelRouteData.setDefaultRouteVersion("v1");
|
||||
|
||||
RouteRule routeRule = new HeaderRule();
|
||||
routeRule.setType("header");
|
||||
routeRule.setCondition("=");
|
||||
routeRule.setKey("tag");
|
||||
routeRule.setValue("gray");
|
||||
RouteRule routeRule1 = new UrlRule.Parameter();
|
||||
routeRule1.setType("parameter");
|
||||
routeRule1.setCondition(">");
|
||||
routeRule1.setKey("id");
|
||||
routeRule1.setValue("10");
|
||||
RouteRule routeRule2 = new UrlRule.Path();
|
||||
routeRule2.setType("path");
|
||||
routeRule2.setCondition("=");
|
||||
routeRule2.setValue("/router-test");
|
||||
routeRules.add(routeRule);
|
||||
routeRules.add(routeRule1);
|
||||
routeRules.add(routeRule2);
|
||||
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion("v2");
|
||||
matchService.setWeight(100);
|
||||
matchService.setRuleList(routeRules);
|
||||
matchServices.add(matchService);
|
||||
|
||||
labelRouteData.setMatchRouteList(matchServices);
|
||||
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
|
||||
|
||||
List<UnifiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
|
||||
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
|
||||
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
|
||||
unifiedRouteDataStructureList));
|
||||
}
|
||||
|
||||
@GetMapping("/update")
|
||||
public void updateDataFromControlPlaneTest() {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
|
||||
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
|
||||
unifiedRouteDataStructure.setTargetService("service-provider");
|
||||
|
||||
LabelRouteRule labelRouteData = new LabelRouteRule();
|
||||
labelRouteData.setDefaultRouteVersion("v1");
|
||||
|
||||
RouteRule routeRule = new HeaderRule();
|
||||
routeRule.setType("header");
|
||||
routeRule.setCondition("=");
|
||||
routeRule.setKey("tag");
|
||||
routeRule.setValue("gray");
|
||||
RouteRule routeRule1 = new UrlRule.Parameter();
|
||||
routeRule1.setType("parameter");
|
||||
routeRule1.setCondition(">");
|
||||
routeRule1.setKey("id");
|
||||
routeRule1.setValue("10");
|
||||
RouteRule routeRule2 = new UrlRule.Path();
|
||||
routeRule2.setType("path");
|
||||
routeRule2.setCondition("=");
|
||||
routeRule2.setValue("/router-test");
|
||||
routeRules.add(routeRule);
|
||||
routeRules.add(routeRule1);
|
||||
routeRules.add(routeRule2);
|
||||
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion("v2");
|
||||
matchService.setWeight(50);
|
||||
matchService.setRuleList(routeRules);
|
||||
matchServices.add(matchService);
|
||||
|
||||
labelRouteData.setMatchRouteList(matchServices);
|
||||
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
|
||||
|
||||
List<UnifiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
|
||||
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
|
||||
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
|
||||
unifiedRouteDataStructureList));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
spring.application.name=service-consumer
|
||||
server.port=18083
|
||||
management.endpoints.web.exposure.include=*
|
||||
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
|
||||
spring.cloud.nacos.discovery.fail-fast=true
|
||||
spring.cloud.nacos.username=nacos
|
||||
spring.cloud.nacos.password=nacos
|
||||
#spring.cloud.governance.router.rule=RandomRule
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>default-provider-version-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Label Routing Provider Example</name>
|
||||
<description>Example demonstrating how to use label routing provider</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import com.alibaba.cloud.nacos.registry.NacosRegistration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class ProviderApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProviderApplication.class, args);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
NacosRegistration nacosRegistration;
|
||||
|
||||
@RestController
|
||||
class Controller {
|
||||
|
||||
@GetMapping("/test")
|
||||
public String test() {
|
||||
String host = nacosRegistration.getHost();
|
||||
int port = nacosRegistration.getPort();
|
||||
String version = nacosRegistration.getMetadata().get("version");
|
||||
return "Route in " + host + ": " + port + ",version is " + version + ".";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
server.port=18081
|
||||
spring.application.name=service-provider
|
||||
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
|
||||
spring.cloud.nacos.discovery.enabled=true
|
||||
spring.cloud.nacos.discovery.metadata.version=v1
|
||||
|
||||
|
||||
spring.cloud.nacos.username=nacos
|
||||
spring.cloud.nacos.password=nacos
|
||||
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoint.health.show-details=always
|
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>label-routing-istio-consumer-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Istio Label Routing Consumer Example</name>
|
||||
<description>Example demonstrating how to use Istio label routing consumer</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient(autoRegister = true)
|
||||
@EnableFeignClients
|
||||
public class IstioConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(IstioConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
@FeignClient(name = "service-provider")
|
||||
public interface FeignService {
|
||||
|
||||
/**
|
||||
* Feign test.
|
||||
* @return String
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
String test();
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class Controller {
|
||||
|
||||
@Autowired
|
||||
private IstioConsumerApplication.FeignService feignService;
|
||||
|
||||
@GetMapping("/istio-label-routing")
|
||||
public String labelRouting() {
|
||||
return feignService.test();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
server:
|
||||
port: 18084
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
application:
|
||||
name: service-consumer
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
fail-fast: true
|
||||
username: nacos
|
||||
password: nacos
|
||||
governance:
|
||||
auth:
|
||||
enabled: ${ISTIO_AUTH_ENABLE:false}
|
||||
istio:
|
||||
config:
|
||||
enabled: ${ISTIO_CONFIG_ENABLE:true}
|
||||
host: ${ISTIOD_ADDR:127.0.0.1}
|
||||
port: ${ISTIOD_PORT:15010}
|
||||
polling-pool-size: ${POLLING_POOL_SIZE:10}
|
||||
polling-time: ${POLLING_TIME:10}
|
||||
istiod-token: ${ISTIOD_TOKEN:}
|
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>opensergo-consumer-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba OpenSergo Label Routing Consumer Example</name>
|
||||
<description>Example demonstrating how to use OpenSergo label routing consumer</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient(autoRegister = true)
|
||||
@EnableFeignClients
|
||||
public class OpenSergoConsumerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OpenSergoConsumerApplication.class, args);
|
||||
}
|
||||
|
||||
@FeignClient(name = "service-provider")
|
||||
public interface FeignService {
|
||||
|
||||
/**
|
||||
* Feign test.
|
||||
* @return String
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
String test();
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
public class Controller {
|
||||
|
||||
@Autowired
|
||||
private OpenSergoConsumerApplication.FeignService feignService;
|
||||
|
||||
@GetMapping("/router-test")
|
||||
public String labelRouting() {
|
||||
return feignService.test();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
spring.application.name=service-consumer
|
||||
server.port=18083
|
||||
management.endpoints.web.exposure.include=*
|
||||
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
|
||||
spring.cloud.nacos.discovery.fail-fast=true
|
||||
spring.cloud.nacos.username=nacos
|
||||
spring.cloud.nacos.password=nacos
|
||||
spring.cloud.opensergo.endpoint=127.0.0.1:10246
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>provider-version-example</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Label Routing Provider Example</name>
|
||||
<description>Example demonstrating how to use label routing provider</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.examples;
|
||||
|
||||
import com.alibaba.cloud.nacos.registry.NacosRegistration;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class ProviderApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ProviderApplication.class, args);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
NacosRegistration nacosRegistration;
|
||||
|
||||
@RestController
|
||||
class Controller {
|
||||
|
||||
@GetMapping("/test")
|
||||
public String test() {
|
||||
String host = nacosRegistration.getHost();
|
||||
int port = nacosRegistration.getPort();
|
||||
String version = nacosRegistration.getMetadata().get("version");
|
||||
return "Route in " + host + ": " + port + ",version is " + version + ".";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
server.port=18082
|
||||
spring.application.name=service-provider
|
||||
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
|
||||
spring.cloud.nacos.discovery.enabled=true
|
||||
spring.cloud.nacos.discovery.metadata.version=v2
|
||||
|
||||
spring.cloud.nacos.username=nacos
|
||||
spring.cloud.nacos.password=nacos
|
||||
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoint.health.show-details=always
|
@ -0,0 +1,355 @@
|
||||
# label route example
|
||||
|
||||
## Project Description
|
||||
|
||||
This project demonstrates how to use the spring cloud ailbaba governance labelrouting module to complete the label routing function.
|
||||
|
||||
## module structure
|
||||
|
||||
This module includes a consumer instance and a provider cluster, which contains two instances.
|
||||
|
||||
## Example
|
||||
|
||||
### How to access
|
||||
|
||||
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
|
||||
1. First, modify the pom XML file, which introduces the spring cloud ailbaba governance labelrouting dependency.
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### Application Start
|
||||
|
||||
Start a startup class of three modules, ConsumerApplication and two ProviderApplications, and inject them into the Nacos registry.
|
||||
|
||||
### Effect demonstration
|
||||
|
||||
#### Rule Description
|
||||
The rules set in the instance are as follows:
|
||||
```java
|
||||
@GetMapping("/add")
|
||||
public void getDataFromControlPlaneTest() {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
UnifiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
|
||||
unifiedRouteDataStructure.setTargetService("service-provider");
|
||||
LabelRouteRule labelRouteData = new LabelRouteRule();
|
||||
labelRouteData.setDefaultRouteVersion("v1");
|
||||
RouteRule routeRule = new HeaderRule();
|
||||
routeRule.setType("header");
|
||||
routeRule.setCondition("=");
|
||||
routeRule.setKey("tag");
|
||||
routeRule.setValue("gray");
|
||||
RouteRule routeRule1 = new UrlRule.Parameter();
|
||||
routeRule1.setType("parameter");
|
||||
routeRule1.setCondition(">");
|
||||
routeRule1.setKey("id");
|
||||
routeRule1.setValue("10");
|
||||
RouteRule routeRule2 = new UrlRule.Path();
|
||||
routeRule2.setType("path");
|
||||
routeRule2.setCondition("=");
|
||||
routeRule2.setValue("/router-test");
|
||||
routeRules.add(routeRule);
|
||||
routeRules.add(routeRule1);
|
||||
routeRules.add(routeRule2);
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion("v2");
|
||||
matchService.setWeight(100);
|
||||
matchService.setRuleList(routeRules);
|
||||
matchServices.add(matchService);
|
||||
labelRouteData.setMatchRouteList(matchServices);
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
|
||||
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
|
||||
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
|
||||
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
|
||||
}
|
||||
```
|
||||
The rules corresponding to the code are as follows:
|
||||
If the request parameter contains tag=gray and the request header contains id and the value is greater than 10, uri is /router-test at the same time, the traffic is routed to the v2 version. If one of the request parameters does not meet the requirement, the traffic is routed to the v1 version.
|
||||
|
||||
Rules also support dynamic modification. The rules for testing dynamic modification are as follows:
|
||||
```java
|
||||
@GetMapping("/add")
|
||||
public void getDataFromControlPlaneTest() {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
UntiedRouteDataStructure unifiedRouteDataStructure = new UntiedRouteDataStructure();
|
||||
unifiedRouteDataStructure.setTargetService("service-provider");
|
||||
LabelRouteRule labelRouteData = new LabelRouteRule();
|
||||
labelRouteData.setDefaultRouteVersion("v1");
|
||||
|
||||
RouteRule routeRule = new HeaderRule();
|
||||
routeRule.setType("header");
|
||||
routeRule.setCondition("=");
|
||||
routeRule.setKey("tag");
|
||||
routeRule.setValue("gray");
|
||||
RouteRule routeRule1 = new UrlRule.Parameter();
|
||||
routeRule1.setType("parameter");
|
||||
routeRule1.setCondition(">");
|
||||
routeRule1.setKey("id");
|
||||
routeRule1.setValue("10");
|
||||
|
||||
RouteRule routeRule2 = new UrlRule.Path();
|
||||
routeRule2.setType("path");
|
||||
routeRule2.setCondition("=");
|
||||
routeRule2.setValue("/router-test");
|
||||
routeRules.add(routeRule);
|
||||
routeRules.add(routeRule1);
|
||||
routeRules.add(routeRule2);
|
||||
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion("v2");
|
||||
matchService.setWeight(50);
|
||||
matchService.setRuleList(routeRules);
|
||||
matchServices.add(matchService);
|
||||
labelRouteData.setMatchRouteList(matchServices);
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteData);
|
||||
List<UntiedRouteDataStructure> unifiedRouteDataStructureList = new ArrayList<>();
|
||||
unifiedRouteDataStructureList.add(unifiedRouteDataStructure);
|
||||
controlPlaneConnection.pushRouteData(unifiedRouteDataStructureList);
|
||||
}
|
||||
```
|
||||
The rules corresponding to the code are as follows:
|
||||
If the request parameter contains tag=gray, and the request header contains id and the value is greater than 10,uri is /router-test, 50% of the traffic is routed to the v2 version, and the rest is routed to the v1 version. If one of the traffic does not meet the requirements, the traffic is routed to the v1 version.
|
||||
|
||||
##### demonstrate Steps
|
||||
1. visit http://localhost:18083/add Push the routing rules from the control surface interface to the routing rule warehouse
|
||||
visit http://localhost:18083/router -The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results:
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
||||
visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. The route is to the v2 version. The v2 version instance prints and returns the following results:
|
||||
```
|
||||
Route in 30.221.132.228: 18082,version is v2.
|
||||
```
|
||||
|
||||
2. visit http://localhost:18083/update Simulate dynamic modification of routing rules.
|
||||
visit http://localhost:18083/router The test does not meet the routing rules. When the test is routed to the v1 version, the v1 version instance prints and returns the following results:
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
||||
visit http://localhost:18083/router-test?id=11 and the test value set in the request header is gray, which meets the routing rules. 50% of the routes are routed to the v2 version. The v2 version instance prints the following results:
|
||||
```
|
||||
Route in 30.221.132.228: 18082,version is v2.
|
||||
```
|
||||
50% of them are routed to the v1 version, and the following results are returned when the v1 version instance is printed:
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
||||
|
||||
3. If you don't push rule,it will load balance by common rule you set.
|
||||
## Integrating Istio
|
||||
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
|
||||
## Preparation
|
||||
### Install K8s
|
||||
Please refer to [tools](https://kubernetes.io/zh-cn/docs/tasks/tools/) chapter of K8s document.
|
||||
### Enable Istio on K8s
|
||||
Please refer to [install](https://istio.io/latest/zh/docs/setup/install/) chapter of Istio document.
|
||||
### Introduction to Istio traffic control rules
|
||||
- [overview](https://istio.io/latest/zh/docs/concepts/security/#authorization)
|
||||
- [detail](https://istio.io/latest/zh/docs/reference/config/security/)
|
||||
1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-istio` dependency
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. Configure application.yml for Istio control plane:
|
||||
```
|
||||
server:
|
||||
port: 18084
|
||||
spring:
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
application:
|
||||
name: service-consumer
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
fail-fast: true
|
||||
username: nacos
|
||||
password: nacos
|
||||
governance:
|
||||
auth:
|
||||
# Is authentication enabled
|
||||
enabled: ${ISTIO_AUTH_ENABLE:false}
|
||||
istio:
|
||||
config:
|
||||
# Is Istio resource transform enabled
|
||||
enabled: ${ISTIO_CONFIG_ENABLE:true}
|
||||
# Istiod ip
|
||||
host: ${ISTIOD_ADDR:127.0.0.1}
|
||||
# Istiod port
|
||||
port: ${ISTIOD_PORT:15010}
|
||||
# Istiod thread-pool size
|
||||
polling-pool-size: ${POLLING_POOL_SIZE:10}
|
||||
# Istiod polling gap
|
||||
polling-time: ${POLLING_TIME:10}
|
||||
# Istiod token(For Istio 15012 port)
|
||||
istiod-token: ${ISTIOD_TOKEN:}
|
||||
# Whether to print xds log
|
||||
log-xds: ${LOG_XDS:true}
|
||||
```
|
||||
### Startup Application
|
||||
Start IstioConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center.
|
||||
|
||||
### Publish Configuration
|
||||
We publish the label routing rules through the Istio control plane. We publish a DestinationRule rule first:
|
||||
```
|
||||
kubectl apply -f - << EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: my-destination-rule
|
||||
spec:
|
||||
host: sca-virtual-service
|
||||
subsets:
|
||||
- name: v1
|
||||
labels:
|
||||
version: v1
|
||||
- name: v2
|
||||
labels:
|
||||
version: v2
|
||||
EOF
|
||||
```
|
||||
This rule splits the back-end service into two versions. Pod with label v1 is assigned to v1, and pod with label v2 is assigned to v2
|
||||
After that, we publish the VirtualService rule:
|
||||
```
|
||||
kubectl apply -f - << EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: sca-virtual-service
|
||||
spec:
|
||||
hosts:
|
||||
- service-provider
|
||||
http:
|
||||
- match:
|
||||
- headers:
|
||||
tag:
|
||||
exact: gray
|
||||
uri:
|
||||
exact: /istio-label-routing
|
||||
route:
|
||||
- destination:
|
||||
host: service-provider
|
||||
subset: v2
|
||||
- route:
|
||||
- destination:
|
||||
host: service-provider
|
||||
subset: v1
|
||||
EOF
|
||||
```
|
||||
This VirtualService specifies the simplest label routing rule. HTTP requests with a gray header and /istio-label-routing path are routed to v2, and the rest of the traffic is routed to v1:
|
||||
### Demonstrate effect
|
||||
We send an HTTP request without a request header to IstioConsumerApplication:
|
||||
```
|
||||
curl --location --request GET '127.0.0.1:18084/istio-label-routing'
|
||||
```
|
||||
Since the request header is not gray, the request will be routed to version v1 with the following result:
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
||||
We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing:
|
||||
```
|
||||
curl --location --request GET '127.0.0.1:18084/istio-label-routing' --header 'tag: gray'
|
||||
```
|
||||
The request is routed to version v2 because the routing rule is matched by the request:
|
||||
```
|
||||
Route in 30.221.132.228: 18082,version is v2.
|
||||
```
|
||||
Finally, we delete this label routing rule::
|
||||
```shell
|
||||
kubectl delete VirtualService sca-virtual-service
|
||||
kubectl delete DestinationRule my-destination-rule
|
||||
```
|
||||
After the rule is deleted, the routing policy is not determined by whether the request header is carried or not, but completely depends on the implementation of the loadbalancer。
|
||||
## Integrating OpenSergo
|
||||
**Note that this section is only for your convenience in understanding the access method. The access work has been completed in this sample code, and you do not need to modify it.**
|
||||
### Configure
|
||||
1. First, modify the pom.xml file to introduce the `spring-cloud-starter-alibaba-governance-routing` and `spring-cloud-starter-alibaba-controlplane-opensergo` dependency
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
2. Configure application.yml for OpenSergo control plane
|
||||
```
|
||||
# The endpoint of OpenSergo ControlPlane
|
||||
spring.cloud.opensergo.endpoint=127.0.0.1:10246
|
||||
```
|
||||
### Startup Application
|
||||
Start OpenSergoConsumerApplication and two ProviderApplications, and inject it into the Nacos registry center.
|
||||
### Publish Configuration
|
||||
[First start OpenSergo control plan](https://opensergo.io/docs/quick-start/opensergo-control-plane/) , Then we publish the label routing rules through the OpenSergo control plane. We publish a TrafficRouter rule.
|
||||
```
|
||||
kubectl apply -f - << EOF
|
||||
apiVersion: traffic.opensergo.io/v1alpha1
|
||||
kind: TrafficRouter
|
||||
metadata:
|
||||
name: service-provider
|
||||
namespace: default
|
||||
labels:
|
||||
app: service-provider
|
||||
spec:
|
||||
hosts:
|
||||
- service-provider
|
||||
http:
|
||||
- match:
|
||||
- headers:
|
||||
tag:
|
||||
exact: v2
|
||||
route:
|
||||
- destination:
|
||||
host: service-provider
|
||||
subset: v2
|
||||
fallback:
|
||||
host: service-provider
|
||||
subset: v1
|
||||
- route:
|
||||
- destination:
|
||||
host: service-provider
|
||||
subset: v1
|
||||
EOF
|
||||
```
|
||||
This TrafficRouter specifies the simplest label routing rule. HTTP requests with a gray header are routed to v2, and the rest of the traffic is routed to v1.
|
||||
If the version v2 does not have a corresponding instance, the HTTP request will fall back to the version v1.
|
||||
### Demonstrate effect
|
||||
We send an HTTP request without a request header to IstioConsumerApplication
|
||||
```
|
||||
curl --location --request GET '127.0.0.1:18083/router-test'
|
||||
```
|
||||
Since the request header is not gray, the request will be routed to version v1 with the following result
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
||||
We then send an HTTP request with a gray tag in its header and the request path is /istio-label-routing
|
||||
```
|
||||
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: gray'
|
||||
```
|
||||
The request is routed to version v2 because the routing rule is matched by the request.
|
||||
```
|
||||
Route in 30.221.132.228: 18082,version is v2.
|
||||
```
|
||||
After we stop the ProviderApplication of the version v2, we send an HTTP request with the request header tag gray.
|
||||
```
|
||||
curl --location --request GET '127.0.0.1:18083/router-test' --header 'tag: v2'
|
||||
```
|
||||
because the version v2 does not have a corresponding instance, so the Http requesr is fallback to the version v1.
|
||||
```
|
||||
Route in 30.221.132.228: 18081,version is v1.
|
||||
```
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.auth.condition;
|
||||
|
||||
import com.alibaba.cloud.commons.matcher.Matcher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class AuthCondition {
|
||||
|
||||
public enum ValidationType {
|
||||
|
||||
/**
|
||||
* All types of auth validation.
|
||||
*/
|
||||
HEADER, SOURCE_IP, REMOTE_IP, DEST_IP, REQUEST_PRINCIPALS, AUTH_AUDIENCES, AUTH_CLAIMS, AUTH_PRESENTERS, HOSTS, PATHS, PORTS, METHODS, IDENTITY
|
||||
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuthCondition.class);
|
||||
|
||||
private ValidationType type;
|
||||
|
||||
private String key;
|
||||
|
||||
private Matcher matcher;
|
||||
|
||||
public AuthCondition(ValidationType type, Matcher matcher) {
|
||||
this.type = type;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
public AuthCondition(ValidationType type, String key, Matcher matcher) {
|
||||
this(type, matcher);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public ValidationType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(ValidationType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Matcher getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
public void setMatcher(Matcher matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.auth.rule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class AuthRule {
|
||||
|
||||
public enum RuleOperation {
|
||||
|
||||
/**
|
||||
* In what way are subrules connected.
|
||||
*/
|
||||
UNKNOWN, AND, OR
|
||||
|
||||
}
|
||||
|
||||
private RuleOperation op = RuleOperation.UNKNOWN;
|
||||
|
||||
private List<AuthRule> children = new ArrayList<>();
|
||||
|
||||
private AuthCondition condition;
|
||||
|
||||
private boolean isNot;
|
||||
|
||||
public AuthRule(RuleOperation op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public AuthRule(RuleOperation op, boolean isNot) {
|
||||
this(op);
|
||||
this.isNot = isNot;
|
||||
}
|
||||
|
||||
public AuthRule(AuthCondition condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public AuthRule(AuthCondition condition, boolean isNot) {
|
||||
this(condition);
|
||||
this.isNot = isNot;
|
||||
}
|
||||
|
||||
public void addChildren(AuthRule rule) {
|
||||
children.add(rule);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
if (children.isEmpty()) {
|
||||
return condition == null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return condition != null;
|
||||
}
|
||||
|
||||
public RuleOperation getOp() {
|
||||
return op;
|
||||
}
|
||||
|
||||
public void setOp(RuleOperation op) {
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public List<AuthRule> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<AuthRule> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public AuthCondition getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void setCondition(AuthCondition condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public boolean isNot() {
|
||||
return isNot;
|
||||
}
|
||||
|
||||
public void setNot(boolean not) {
|
||||
isNot = not;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.auth.rule;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class AuthRules {
|
||||
|
||||
private final Map<String, AuthRule> allowAuthRules;
|
||||
|
||||
private final Map<String, AuthRule> denyAuthRules;
|
||||
|
||||
private final Map<String, JwtRule> jwtRules;
|
||||
|
||||
public AuthRules(Map<String, AuthRule> allowAuthRules,
|
||||
Map<String, AuthRule> denyAuthRules, Map<String, JwtRule> jwtRules) {
|
||||
this.allowAuthRules = allowAuthRules;
|
||||
this.denyAuthRules = denyAuthRules;
|
||||
this.jwtRules = jwtRules;
|
||||
}
|
||||
|
||||
public Map<String, AuthRule> getAllowAuthRules() {
|
||||
return allowAuthRules;
|
||||
}
|
||||
|
||||
public Map<String, AuthRule> getDenyAuthRules() {
|
||||
return denyAuthRules;
|
||||
}
|
||||
|
||||
public Map<String, JwtRule> getJwtRules() {
|
||||
return jwtRules;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.auth.rule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class JwtRule {
|
||||
|
||||
private String name;
|
||||
|
||||
private Map<String, String> fromHeaders;
|
||||
|
||||
private String issuer;
|
||||
|
||||
private List<String> audiences;
|
||||
|
||||
private String jwks;
|
||||
|
||||
private List<String> fromParams;
|
||||
|
||||
private String outputPayloadToHeader;
|
||||
|
||||
private boolean forwardOriginalToken;
|
||||
|
||||
public JwtRule(String name, Map<String, String> fromHeaders, String issuer,
|
||||
List<String> audiences, String jwks, List<String> fromParams,
|
||||
String outputPayloadToHeader, boolean forwardOriginalToken) {
|
||||
this.name = name;
|
||||
this.fromHeaders = fromHeaders;
|
||||
this.issuer = issuer;
|
||||
this.audiences = audiences;
|
||||
this.jwks = jwks;
|
||||
this.fromParams = fromParams;
|
||||
this.outputPayloadToHeader = outputPayloadToHeader;
|
||||
this.forwardOriginalToken = forwardOriginalToken;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Map<String, String> getFromHeaders() {
|
||||
return fromHeaders;
|
||||
}
|
||||
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
public List<String> getAudiences() {
|
||||
return audiences;
|
||||
}
|
||||
|
||||
public String getJwks() {
|
||||
return jwks;
|
||||
}
|
||||
|
||||
public List<String> getFromParams() {
|
||||
return fromParams;
|
||||
}
|
||||
|
||||
public String getOutputPayloadToHeader() {
|
||||
return outputPayloadToHeader;
|
||||
}
|
||||
|
||||
public boolean isForwardOriginalToken() {
|
||||
return forwardOriginalToken;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.event;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class AuthDataChangedEvent extends GovernanceEvent {
|
||||
|
||||
/**
|
||||
* Configuration for authentication.
|
||||
*/
|
||||
private final AuthRules authRules;
|
||||
|
||||
public AuthDataChangedEvent(Object source, AuthRules authRules) {
|
||||
super(source);
|
||||
this.authRules = authRules;
|
||||
}
|
||||
|
||||
public AuthRules getAuthRules() {
|
||||
return authRules;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class GovernanceEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* Create a new {@code ApplicationEvent}.
|
||||
* @param source the object on which the event initially occurred or with which the
|
||||
* event is associated (never {@code null})
|
||||
*/
|
||||
public GovernanceEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.event;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class LabelRoutingDataChangedEvent extends GovernanceEvent {
|
||||
|
||||
/**
|
||||
* Configuration for Label Routing.
|
||||
*/
|
||||
private final Collection<UnifiedRouteDataStructure> untiedRouterDataStructureList;
|
||||
|
||||
public LabelRoutingDataChangedEvent(Object source,
|
||||
Collection<UnifiedRouteDataStructure> untiedRouterDataStructureList) {
|
||||
super(source);
|
||||
this.untiedRouterDataStructureList = untiedRouterDataStructureList;
|
||||
}
|
||||
|
||||
public Collection<UnifiedRouteDataStructure> getUntiedRouterDataStructureList() {
|
||||
return untiedRouterDataStructureList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
public class TargetServiceChangedEvent extends ApplicationEvent {
|
||||
|
||||
public TargetServiceChangedEvent(Object source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public class LabelRouteRule {
|
||||
|
||||
private String defaultRouteVersion;
|
||||
|
||||
private List<MatchService> matchRouteList;
|
||||
|
||||
public String getDefaultRouteVersion() {
|
||||
return defaultRouteVersion;
|
||||
}
|
||||
|
||||
public void setDefaultRouteVersion(String defaultRouteVersion) {
|
||||
this.defaultRouteVersion = defaultRouteVersion;
|
||||
}
|
||||
|
||||
public List<MatchService> getMatchRouteList() {
|
||||
return matchRouteList;
|
||||
}
|
||||
|
||||
public void setMatchRouteList(List<MatchService> matchRouteList) {
|
||||
this.matchRouteList = matchRouteList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
LabelRouteRule that = (LabelRouteRule) o;
|
||||
return Objects.equals(defaultRouteVersion, that.defaultRouteVersion)
|
||||
&& Objects.equals(getMatchRouteList(), that.getMatchRouteList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(defaultRouteVersion, getMatchRouteList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LabelRouteData{" + "defaultRouteVersion='" + defaultRouteVersion + '\''
|
||||
+ ", matchRouteList=" + matchRouteList + '}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public class MatchService {
|
||||
|
||||
private List<RouteRule> ruleList;
|
||||
|
||||
private String version;
|
||||
|
||||
private Integer weight;
|
||||
|
||||
private String fallbackVersion;
|
||||
|
||||
public String getFallback() {
|
||||
return fallbackVersion;
|
||||
}
|
||||
|
||||
public void setFallback(String fallbackVersion) {
|
||||
this.fallbackVersion = fallbackVersion;
|
||||
}
|
||||
|
||||
public List<RouteRule> getRuleList() {
|
||||
return ruleList;
|
||||
}
|
||||
|
||||
public void setRuleList(List<RouteRule> ruleList) {
|
||||
this.ruleList = ruleList;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(Integer weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MatchService that = (MatchService) o;
|
||||
return getWeight().equals(that.getWeight())
|
||||
&& Objects.equals(getRuleList(), that.getRuleList())
|
||||
&& Objects.equals(getFallback(), that.getFallback())
|
||||
&& Objects.equals(getVersion(), that.getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getRuleList(), getVersion(), getWeight(), getFallback());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MatchService{" + "ruleList=" + ruleList + ", version='" + version + '\''
|
||||
+ ", weight=" + weight + ", getFallback=" + fallbackVersion + '}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public class UnifiedRouteDataStructure {
|
||||
|
||||
private LabelRouteRule labelRouteRule;
|
||||
|
||||
private String targetService;
|
||||
|
||||
public LabelRouteRule getLabelRouteRule() {
|
||||
return labelRouteRule;
|
||||
}
|
||||
|
||||
public void setLabelRouteRule(LabelRouteRule labelRouteRule) {
|
||||
this.labelRouteRule = labelRouteRule;
|
||||
}
|
||||
|
||||
public String getTargetService() {
|
||||
return targetService;
|
||||
}
|
||||
|
||||
public void setTargetService(String targetService) {
|
||||
this.targetService = targetService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UntiedRouteDataStructure{" + "labelRouteData=" + labelRouteRule
|
||||
+ ", targetService='" + targetService + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting.rule;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public class HeaderRule implements RouteRule {
|
||||
|
||||
private String type;
|
||||
|
||||
private String condition;
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
HeaderRule that = (HeaderRule) o;
|
||||
return Objects.equals(getType(), that.getType())
|
||||
&& Objects.equals(getCondition(), that.getCondition())
|
||||
&& Objects.equals(getKey(), that.getKey())
|
||||
&& Objects.equals(getValue(), that.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getType(), getCondition(), getKey(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HeaderRule{" + "type='" + type + '\'' + ", condition='" + condition + '\''
|
||||
+ ", key='" + key + '\'' + ", value='" + value + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting.rule;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public interface RouteRule {
|
||||
|
||||
/**
|
||||
* get type of rule.
|
||||
* @return String
|
||||
*/
|
||||
String getType();
|
||||
|
||||
void setType(String type);
|
||||
|
||||
String getCondition();
|
||||
|
||||
void setCondition(String condition);
|
||||
|
||||
String getKey();
|
||||
|
||||
void setKey(String key);
|
||||
|
||||
String getValue();
|
||||
|
||||
void setValue(String value);
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.governance.labelrouting.rule;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author HH
|
||||
*/
|
||||
public class UrlRule {
|
||||
|
||||
public static class Path implements RouteRule {
|
||||
|
||||
private String type;
|
||||
|
||||
private String condition;
|
||||
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(String key) {
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Path path = (Path) o;
|
||||
return Objects.equals(getType(), path.getType())
|
||||
&& Objects.equals(getCondition(), path.getCondition())
|
||||
&& Objects.equals(getValue(), path.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getType(), getCondition(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Path{" + "type='" + type + '\'' + ", condition='" + condition + '\''
|
||||
+ ", value='" + value + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Parameter implements RouteRule {
|
||||
|
||||
private String type;
|
||||
|
||||
private String condition;
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Parameter parameter = (Parameter) o;
|
||||
return Objects.equals(getType(), parameter.getType())
|
||||
&& Objects.equals(getCondition(), parameter.getCondition())
|
||||
&& Objects.equals(getKey(), parameter.getKey())
|
||||
&& Objects.equals(getValue(), parameter.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getType(), getCondition(), getKey(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Parameter{" + "type='" + type + '\'' + ", condition='" + condition
|
||||
+ '\'' + ", key='" + key + '\'' + ", value='" + value + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.matcher;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class IpMatcher implements Matcher {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IpMatcher.class);
|
||||
|
||||
private int prefixLen;
|
||||
|
||||
private String ip;
|
||||
|
||||
public IpMatcher() {
|
||||
|
||||
}
|
||||
|
||||
public IpMatcher(int prefixLen, String ip) {
|
||||
this.prefixLen = prefixLen;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public boolean match(Object object) {
|
||||
if (!(object instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
String ip = (String) object;
|
||||
String ruleIp = ip2BinaryString(this.ip);
|
||||
if (StringUtils.isEmpty(ruleIp)) {
|
||||
return false;
|
||||
}
|
||||
String ipBinary = ip2BinaryString(ip);
|
||||
if (StringUtils.isEmpty(ipBinary)) {
|
||||
return false;
|
||||
}
|
||||
if (prefixLen <= 0) {
|
||||
return ruleIp.equals(ipBinary);
|
||||
}
|
||||
if (ruleIp.length() >= prefixLen && ipBinary.length() >= prefixLen) {
|
||||
return ruleIp.substring(0, prefixLen)
|
||||
.equals(ipBinary.substring(0, prefixLen));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ip dotted ip string, for example: 127.0.0.1
|
||||
* @return
|
||||
*/
|
||||
private String ip2BinaryString(String ip) {
|
||||
try {
|
||||
String[] ips = ip.split("\\.");
|
||||
long[] ipLong = new long[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ipLong[i] = Long.parseLong(ips[i]);
|
||||
if (ipLong[i] < 0 || ipLong[i] > 255) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return String
|
||||
.format("%32s", Long.toBinaryString((ipLong[0] << 24)
|
||||
+ (ipLong[1] << 16) + (ipLong[2] << 8) + ipLong[3]))
|
||||
.replace(" ", "0");
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("failed to parse ip {} to binary string", ip);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public int getPrefixLen() {
|
||||
return prefixLen;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setPrefixLen(int prefixLen) {
|
||||
this.prefixLen = prefixLen;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.matcher;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public interface Matcher {
|
||||
|
||||
boolean match(Object obj);
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.matcher;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class PortMatcher implements Matcher {
|
||||
|
||||
private Integer matcher;
|
||||
|
||||
public PortMatcher() {
|
||||
|
||||
}
|
||||
|
||||
public PortMatcher(Integer matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(Object object) {
|
||||
if (!(object instanceof Integer)) {
|
||||
return false;
|
||||
}
|
||||
return matcher != null && matcher.equals(object);
|
||||
}
|
||||
|
||||
public void setMatcher(Integer matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.matcher;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class StringMatcher implements Matcher {
|
||||
|
||||
private String matcher;
|
||||
|
||||
private StringMatcherType type;
|
||||
|
||||
private boolean isIgnoreCase;
|
||||
|
||||
private String regex;
|
||||
|
||||
public StringMatcher() {
|
||||
|
||||
}
|
||||
|
||||
public StringMatcher(String regex) {
|
||||
this.regex = regex;
|
||||
this.type = StringMatcherType.REGEX;
|
||||
}
|
||||
|
||||
public StringMatcher(String matcher, StringMatcherType type, boolean isIgnoreCase) {
|
||||
this.matcher = matcher;
|
||||
this.type = type;
|
||||
this.isIgnoreCase = isIgnoreCase;
|
||||
}
|
||||
|
||||
public boolean match(Object obj) {
|
||||
if (!(obj instanceof String)) {
|
||||
return false;
|
||||
}
|
||||
String str = (String) obj;
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return false;
|
||||
}
|
||||
if (isIgnoreCase) {
|
||||
str = str.toLowerCase(Locale.ROOT);
|
||||
matcher = matcher.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
switch (type) {
|
||||
case EXACT:
|
||||
return str.equals(matcher);
|
||||
case PREFIX:
|
||||
return str.startsWith(matcher);
|
||||
case SUFFIX:
|
||||
return str.endsWith(matcher);
|
||||
case CONTAIN:
|
||||
return str.contains(matcher);
|
||||
case REGEX:
|
||||
try {
|
||||
return Pattern.matches(regex, str);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
"unsupported string compare operation");
|
||||
}
|
||||
}
|
||||
|
||||
public String getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
public void setMatcher(String matcher) {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
public StringMatcherType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(StringMatcherType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean isIgnoreCase() {
|
||||
return isIgnoreCase;
|
||||
}
|
||||
|
||||
public void setIgnoreCase(boolean ignoreCase) {
|
||||
isIgnoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.commons.matcher;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public enum StringMatcherType {
|
||||
|
||||
/**
|
||||
* exact match.
|
||||
*/
|
||||
EXACT("exact"),
|
||||
/**
|
||||
* prefix match.
|
||||
*/
|
||||
PREFIX("prefix"),
|
||||
/**
|
||||
* suffix match.
|
||||
*/
|
||||
SUFFIX("suffix"),
|
||||
/**
|
||||
* present match.
|
||||
*/
|
||||
PRESENT("present"),
|
||||
/**
|
||||
* regex match.
|
||||
*/
|
||||
REGEX("regex"),
|
||||
/**
|
||||
* contain match.
|
||||
*/
|
||||
CONTAIN("contain");
|
||||
|
||||
/**
|
||||
* type of matcher.
|
||||
*/
|
||||
public final String type;
|
||||
|
||||
StringMatcherType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-starters</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-istio</artifactId>
|
||||
<name>Spring Cloud Alibaba Istio Control Plane</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.envoyproxy.controlplane</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>1.0.36</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.16</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Only for unit test-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.google.protobuf.Struct;
|
||||
import com.google.protobuf.Value;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public final class NodeBuilder {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(NodeBuilder.class);
|
||||
|
||||
private static Node NODE;
|
||||
|
||||
private NodeBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public static Node getNode() {
|
||||
try {
|
||||
if (NODE != null) {
|
||||
return NODE;
|
||||
}
|
||||
String podName = System.getenv(IstioConstants.POD_NAME);
|
||||
if (podName == null) {
|
||||
podName = IstioConstants.DEFAULT_POD_NAME;
|
||||
}
|
||||
String podNamespace = System.getenv(IstioConstants.NAMESPACE_NAME);
|
||||
if (podNamespace == null) {
|
||||
podNamespace = IstioConstants.DEFAULT_NAMESPACE;
|
||||
}
|
||||
String ip = "127.0.0.1";
|
||||
try {
|
||||
InetAddress local = InetAddress.getLocalHost();
|
||||
ip = local.getHostAddress();
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
log.error("can not get local ip", e);
|
||||
}
|
||||
Struct.Builder metaBuilder = Struct.newBuilder();
|
||||
// metadata is necessary for RequestAuthentication CRD
|
||||
metaBuilder.putFields("NAMESPACE",
|
||||
Value.newBuilder().setStringValue(podNamespace).build());
|
||||
NODE = Node.newBuilder()
|
||||
.setId(String.format(
|
||||
"sidecar~%s~%s.%s~%s" + IstioConstants.SVC_CLUSTER_LOCAL, ip,
|
||||
podName, podNamespace, podNamespace))
|
||||
.setMetadata(metaBuilder.build()).build();
|
||||
return NODE;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("unable to create node for xds request", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
|
||||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> PilotExchanger is the class which
|
||||
* communicate with istio pilot.
|
||||
*/
|
||||
public class PilotExchanger {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PilotExchanger.class);
|
||||
|
||||
private final LdsProtocol ldsProtocol;
|
||||
|
||||
private final CdsProtocol cdsProtocol;
|
||||
|
||||
private final EdsProtocol edsProtocol;
|
||||
|
||||
private final RdsProtocol rdsProtocol;
|
||||
|
||||
private void observeListeners(List<Listener> listeners) {
|
||||
if (listeners == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> resourceName = ldsProtocol.getResourceNames();
|
||||
if (resourceName != null && !resourceName.isEmpty()) {
|
||||
rdsProtocol.observeResource(resourceName, this::observeRoutes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void observeClusters(List<Cluster> clusters) {
|
||||
Set<String> resourceName = cdsProtocol.getResourceNames();
|
||||
if (resourceName != null && !resourceName.isEmpty()) {
|
||||
// eds
|
||||
edsProtocol.observeResource(resourceName, this::observeEndpoints);
|
||||
}
|
||||
else {
|
||||
// lds
|
||||
ldsProtocol.observeResource(null, this::observeListeners);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void observeEndpoints(List<ClusterLoadAssignment> endpoints) {
|
||||
ldsProtocol.observeResource(null, this::observeListeners);
|
||||
|
||||
}
|
||||
|
||||
private void observeRoutes(List<RouteConfiguration> routes) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("A Xds configuration update is finished");
|
||||
}
|
||||
}
|
||||
|
||||
public PilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol,
|
||||
EdsProtocol edsProtocol, RdsProtocol rdsProtocol) {
|
||||
this.ldsProtocol = ldsProtocol;
|
||||
this.cdsProtocol = cdsProtocol;
|
||||
this.edsProtocol = edsProtocol;
|
||||
this.rdsProtocol = rdsProtocol;
|
||||
// observe cluster first, and update the other xds sequentially
|
||||
this.ldsProtocol.setNeedPolling(false);
|
||||
this.edsProtocol.setNeedPolling(false);
|
||||
this.rdsProtocol.setNeedPolling(false);
|
||||
// only polling cds, other protocol will be obtained sequentially
|
||||
this.cdsProtocol.setNeedPolling(true);
|
||||
cdsProtocol.observeResource(null, this::observeClusters);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.event.GovernanceEvent;
|
||||
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.filter.impl.AuthXdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.filter.impl.LabelRoutingXdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.CdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.EdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.istio.config.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(XdsConfigProperties.class)
|
||||
// We need to auto config the class after all the governance data listener, to prevent
|
||||
// event publisher hang permanently.
|
||||
@AutoConfigureOrder(XdsAutoConfiguration.RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER)
|
||||
public class XdsAutoConfiguration {
|
||||
|
||||
/**
|
||||
* xds auto configuration log.
|
||||
*/
|
||||
private static final Logger log = LoggerFactory.getLogger(XdsAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* Order of xds auto config.
|
||||
*/
|
||||
public static final int RESOURCE_TRANSFORM_AUTO_CONFIG_ORDER = 100;
|
||||
|
||||
@Autowired
|
||||
private XdsConfigProperties xdsConfigProperties;
|
||||
|
||||
@Bean
|
||||
public DummyGovernanceDataListener dummyGovernanceDataListener() {
|
||||
return new DummyGovernanceDataListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XdsChannel xdsChannel() {
|
||||
return new XdsChannel(xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XdsScheduledThreadPool xdsScheduledThreadPool() {
|
||||
return new XdsScheduledThreadPool(xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XdsResolveFilter<List<Listener>> authXdsResolveFilter() {
|
||||
return new AuthXdsResolveFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public XdsResolveFilter<List<RouteConfiguration>> labelRoutingXdsResolveFilter() {
|
||||
return new LabelRoutingXdsResolveFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PilotExchanger pilotExchanger(LdsProtocol ldsProtocol, CdsProtocol cdsProtocol,
|
||||
EdsProtocol edsProtocol, RdsProtocol rdsProtocol) {
|
||||
return new PilotExchanger(ldsProtocol, cdsProtocol, edsProtocol, rdsProtocol);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LdsProtocol ldsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
List<XdsResolveFilter<List<Listener>>> filters) {
|
||||
return new LdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties,
|
||||
filters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CdsProtocol cdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool) {
|
||||
return new CdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EdsProtocol edsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool) {
|
||||
return new EdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RdsProtocol rdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
List<XdsResolveFilter<List<RouteConfiguration>>> filters) {
|
||||
return new RdsProtocol(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties,
|
||||
filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* To prevent the event publish hang permanently.
|
||||
*/
|
||||
private final class DummyGovernanceDataListener
|
||||
implements ApplicationListener<GovernanceEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(GovernanceEvent event) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received governance event " + event.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.alibaba.cloud.commons.io.FileUtils;
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.AggregatedDiscoveryServiceGrpc;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.NegotiationType;
|
||||
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
|
||||
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
import io.grpc.stub.MetadataUtils;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.alibaba.cloud.governance.istio.constant.IstioConstants.ISTIOD_SECURE_PORT;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class XdsChannel implements AutoCloseable {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(XdsChannel.class);
|
||||
|
||||
private ManagedChannel channel;
|
||||
|
||||
private String istiodToken;
|
||||
|
||||
private final XdsConfigProperties xdsConfigProperties;
|
||||
|
||||
public XdsChannel(XdsConfigProperties xdsConfigProperties) {
|
||||
this.xdsConfigProperties = xdsConfigProperties;
|
||||
try {
|
||||
if (xdsConfigProperties.getPort() == ISTIOD_SECURE_PORT) {
|
||||
// fetch token first
|
||||
if (StringUtils.isNotEmpty(xdsConfigProperties.getIstiodToken())) {
|
||||
istiodToken = xdsConfigProperties.getIstiodToken();
|
||||
}
|
||||
else {
|
||||
this.refreshIstiodToken();
|
||||
}
|
||||
SslContext sslcontext = GrpcSslContexts.forClient()
|
||||
// if server's cert doesn't chain to a standard root
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||
// TODO: fill the publicKey and privateKey
|
||||
.build();
|
||||
this.channel = NettyChannelBuilder
|
||||
.forTarget(xdsConfigProperties.getHost() + ":"
|
||||
+ xdsConfigProperties.getPort())
|
||||
.negotiationType(NegotiationType.TLS).sslContext(sslcontext)
|
||||
.build();
|
||||
}
|
||||
else {
|
||||
this.channel = NettyChannelBuilder
|
||||
.forTarget(xdsConfigProperties.getHost() + ":"
|
||||
+ xdsConfigProperties.getPort())
|
||||
.negotiationType(NegotiationType.PLAINTEXT).build();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("create XdsChannel failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshIstiodToken() {
|
||||
if (xdsConfigProperties.getPort() != ISTIOD_SECURE_PORT) {
|
||||
return;
|
||||
}
|
||||
File saFile = new File(IstioConstants.THIRD_PART_JWT_PATH);
|
||||
if (saFile.canRead()) {
|
||||
try {
|
||||
this.istiodToken = FileUtils.readFileToString(saFile,
|
||||
StandardCharsets.UTF_8);
|
||||
return;
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Unable to read token file", e);
|
||||
}
|
||||
}
|
||||
if (this.istiodToken == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unable to found kubernetes service account token file. "
|
||||
+ "Please check if work in Kubernetes and mount service account token file correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (channel != null) {
|
||||
channel.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public StreamObserver<DiscoveryRequest> createDiscoveryRequest(
|
||||
StreamObserver<DiscoveryResponse> observer) {
|
||||
if (channel == null) {
|
||||
return null;
|
||||
}
|
||||
AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub = AggregatedDiscoveryServiceGrpc
|
||||
.newStub(channel);
|
||||
Metadata header = new Metadata();
|
||||
Metadata.Key<String> key = Metadata.Key.of("authorization",
|
||||
Metadata.ASCII_STRING_MARSHALLER);
|
||||
header.put(key, "Bearer " + this.istiodToken);
|
||||
stub = MetadataUtils.attachHeaders(stub, header);
|
||||
return stub.streamAggregatedResources(observer);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@ConfigurationProperties(XdsConfigProperties.PREFIX)
|
||||
public class XdsConfigProperties {
|
||||
|
||||
/**
|
||||
* Prefix in yaml.
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.istio.config";
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private int pollingPoolSize;
|
||||
|
||||
private int pollingTime;
|
||||
|
||||
/**
|
||||
* jwt token for istiod 15012 port.
|
||||
*/
|
||||
private String istiodToken;
|
||||
|
||||
private Boolean logXds;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.port <= 0 || this.port > 65535) {
|
||||
this.port = IstioConstants.ISTIOD_SECURE_PORT;
|
||||
}
|
||||
if (StringUtils.isEmpty(host)) {
|
||||
this.host = IstioConstants.DEFAULT_ISTIOD_ADDR;
|
||||
}
|
||||
if (pollingPoolSize <= 0) {
|
||||
pollingPoolSize = IstioConstants.DEFAULT_POLLING_SIZE;
|
||||
}
|
||||
if (pollingTime <= 0) {
|
||||
pollingTime = IstioConstants.DEFAULT_POLLING_TIME;
|
||||
}
|
||||
if (logXds == null) {
|
||||
logXds = true;
|
||||
}
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public int getPollingPoolSize() {
|
||||
return pollingPoolSize;
|
||||
}
|
||||
|
||||
public void setPollingPoolSize(int pollingPoolSize) {
|
||||
this.pollingPoolSize = pollingPoolSize;
|
||||
}
|
||||
|
||||
public int getPollingTime() {
|
||||
return pollingTime;
|
||||
}
|
||||
|
||||
public void setPollingTime(int pollingTime) {
|
||||
this.pollingTime = pollingTime;
|
||||
}
|
||||
|
||||
public String getIstiodToken() {
|
||||
return istiodToken;
|
||||
}
|
||||
|
||||
public void setIstiodToken(String istiodToken) {
|
||||
this.istiodToken = istiodToken;
|
||||
}
|
||||
|
||||
public boolean isLogXds() {
|
||||
return Boolean.TRUE.equals(logXds);
|
||||
}
|
||||
|
||||
public void setLogXds(boolean logXds) {
|
||||
this.logXds = logXds;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class XdsScheduledThreadPool extends ScheduledThreadPoolExecutor {
|
||||
|
||||
public XdsScheduledThreadPool(XdsConfigProperties xdsConfigProperties) {
|
||||
this(xdsConfigProperties.getPollingPoolSize());
|
||||
}
|
||||
|
||||
public XdsScheduledThreadPool(int corePoolSize) {
|
||||
super(corePoolSize);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.constant;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public final class IstioConstants {
|
||||
|
||||
/**
|
||||
* Suffix of node.
|
||||
*/
|
||||
public static final String SVC_CLUSTER_LOCAL = ".svc.cluster.local";
|
||||
|
||||
/**
|
||||
* Default pod name.
|
||||
*/
|
||||
public static final String DEFAULT_POD_NAME = "sidecar";
|
||||
|
||||
/**
|
||||
* Default namespace name.
|
||||
*/
|
||||
public static final String DEFAULT_NAMESPACE = "default";
|
||||
|
||||
/**
|
||||
* Key of pod name.
|
||||
*/
|
||||
public static final String POD_NAME = "POD_NAME";
|
||||
|
||||
/**
|
||||
* Key of namespace name.
|
||||
*/
|
||||
public static final String NAMESPACE_NAME = "NAMESPACE_NAME";
|
||||
|
||||
/**
|
||||
* third-part jwt token location.
|
||||
*/
|
||||
public static final String THIRD_PART_JWT_PATH = "/var/run/secrets/tokens/istio-token";
|
||||
|
||||
/**
|
||||
* url of cds request.
|
||||
*/
|
||||
public static final String CDS_URL = "type.googleapis.com/envoy.config.cluster.v3.Cluster";
|
||||
|
||||
/**
|
||||
* url of eds request.
|
||||
*/
|
||||
public static final String EDS_URL = "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment";
|
||||
|
||||
/**
|
||||
* url of lds request.
|
||||
*/
|
||||
public static final String LDS_URL = "type.googleapis.com/envoy.config.listener.v3.Listener";
|
||||
|
||||
/**
|
||||
* url of rds request.
|
||||
*/
|
||||
public static final String RDS_URL = "type.googleapis.com/envoy.config.route.v3.RouteConfiguration";
|
||||
|
||||
/**
|
||||
* secure port of istiod.
|
||||
*/
|
||||
public static final int ISTIOD_SECURE_PORT = 15012;
|
||||
|
||||
/**
|
||||
* default polling size of xds request.
|
||||
*/
|
||||
public static final int DEFAULT_POLLING_SIZE = 10;
|
||||
|
||||
/**
|
||||
* default polling time of xds request.
|
||||
*/
|
||||
public static final int DEFAULT_POLLING_TIME = 30;
|
||||
|
||||
/**
|
||||
* default ip address of istiod.
|
||||
*/
|
||||
public static final String DEFAULT_ISTIOD_ADDR = "127.0.0.1";
|
||||
|
||||
private IstioConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.filter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public abstract class AbstractXdsResolveFilter<T>
|
||||
implements XdsResolveFilter<T>, ApplicationContextAware {
|
||||
|
||||
protected static final Logger log = LoggerFactory
|
||||
.getLogger(AbstractXdsResolveFilter.class);
|
||||
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
protected static final String ALLOW_ANY = "allow_any";
|
||||
|
||||
protected static final String PATH = "path";
|
||||
|
||||
protected static final String VIRTUAL_INBOUND = "virtualInbound";
|
||||
|
||||
protected static final String CONNECTION_MANAGER = "envoy.filters.network.http_connection_manager";
|
||||
|
||||
protected static final String RBAC_FILTER = "envoy.filters.http.rbac";
|
||||
|
||||
protected static final String JWT_FILTER = "envoy.filters.http.jwt_authn";
|
||||
|
||||
protected static final String ISTIO_AUTHN = "istio_authn";
|
||||
|
||||
protected static final String REQUEST_AUTH_PRINCIPAL = "request.auth.principal";
|
||||
|
||||
protected static final String REQUEST_AUTH_AUDIENCE = "request.auth.audiences";
|
||||
|
||||
protected static final String REQUEST_AUTH_PRESENTER = "request.auth.presenter";
|
||||
|
||||
protected static final String REQUEST_AUTH_CLAIMS = "request.auth.claims";
|
||||
|
||||
protected static final String HEADER_NAME_AUTHORITY = ":authority";
|
||||
|
||||
protected static final String HEADER_NAME_METHOD = ":method";
|
||||
|
||||
protected static final int MIN_PORT = 0;
|
||||
|
||||
protected static final int MAX_PORT = 65535;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.filter;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public interface XdsResolveFilter<T> {
|
||||
|
||||
boolean resolve(T t);
|
||||
|
||||
String getTypeUrl();
|
||||
|
||||
}
|
@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.filter.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.auth.condition.AuthCondition;
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.AuthRule;
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.JwtRule;
|
||||
import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent;
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.commons.matcher.PortMatcher;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcher;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.util.ConvUtil;
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.listener.v3.FilterChain;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.config.rbac.v3.Permission;
|
||||
import io.envoyproxy.envoy.config.rbac.v3.Policy;
|
||||
import io.envoyproxy.envoy.config.rbac.v3.Principal;
|
||||
import io.envoyproxy.envoy.config.rbac.v3.RBAC;
|
||||
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication;
|
||||
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtHeader;
|
||||
import io.envoyproxy.envoy.extensions.filters.http.jwt_authn.v3.JwtProvider;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpFilter;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.MetadataMatcher;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class AuthXdsResolveFilter extends AbstractXdsResolveFilter<List<Listener>> {
|
||||
|
||||
@Override
|
||||
public boolean resolve(List<Listener> listeners) {
|
||||
if (listeners == null || listeners.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Map<String, AuthRule> allowAuthRules = new HashMap<>();
|
||||
Map<String, AuthRule> denyAuthRules = new HashMap<>();
|
||||
Map<String, JwtRule> jwtRules = new HashMap<>();
|
||||
List<HttpFilter> httpFilters = resolveHttpFilter(listeners);
|
||||
List<RBAC> rbacList = resolveRbac(httpFilters);
|
||||
for (RBAC rbac : rbacList) {
|
||||
for (Map.Entry<String, Policy> entry : rbac.getPoliciesMap().entrySet()) {
|
||||
AuthRule authRule = new AuthRule(AuthRule.RuleOperation.AND);
|
||||
AuthRule principalOr = new AuthRule(AuthRule.RuleOperation.OR);
|
||||
// principals
|
||||
List<Principal> principals = entry.getValue().getPrincipalsList();
|
||||
for (Principal principal : principals) {
|
||||
AuthRule principalAnd = resolvePrincipal(principal);
|
||||
if (principalAnd != null && !principalAnd.isEmpty()) {
|
||||
principalOr.addChildren(principalAnd);
|
||||
}
|
||||
}
|
||||
// permission
|
||||
AuthRule permissionOr = new AuthRule(AuthRule.RuleOperation.OR);
|
||||
List<Permission> permissions = entry.getValue().getPermissionsList();
|
||||
for (Permission permission : permissions) {
|
||||
AuthRule permissionAnd = resolvePermission(permission);
|
||||
if (permissionAnd != null && !permissionAnd.isEmpty()) {
|
||||
permissionOr.addChildren(permissionAnd);
|
||||
}
|
||||
}
|
||||
if (!principalOr.isEmpty()) {
|
||||
authRule.addChildren(principalOr);
|
||||
}
|
||||
if (!permissionOr.isEmpty()) {
|
||||
authRule.addChildren(permissionOr);
|
||||
}
|
||||
if (authRule.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
switch (rbac.getAction()) {
|
||||
case UNRECOGNIZED:
|
||||
case ALLOW:
|
||||
allowAuthRules.put(entry.getKey(), authRule);
|
||||
break;
|
||||
case DENY:
|
||||
denyAuthRules.put(entry.getKey(), authRule);
|
||||
break;
|
||||
default:
|
||||
log.warn("Unknown rbac action, {}", rbac.getAction());
|
||||
}
|
||||
}
|
||||
}
|
||||
List<JwtAuthentication> jwtAuthentications = resolveJWT(httpFilters);
|
||||
for (JwtAuthentication jwtRule : jwtAuthentications) {
|
||||
Map<String, JwtProvider> jwtProviders = jwtRule.getProvidersMap();
|
||||
for (Map.Entry<String, JwtProvider> entry : jwtProviders.entrySet()) {
|
||||
JwtProvider provider = entry.getValue();
|
||||
Map<String, String> fromHeaders = new HashMap<>();
|
||||
for (JwtHeader header : provider.getFromHeadersList()) {
|
||||
fromHeaders.put(header.getName(), header.getValuePrefix());
|
||||
}
|
||||
jwtRules.put(entry.getKey(),
|
||||
new JwtRule(entry.getKey(), fromHeaders, provider.getIssuer(),
|
||||
new ArrayList<>(provider.getAudiencesList()),
|
||||
provider.getLocalJwks().getInlineString(),
|
||||
new ArrayList<>(provider.getFromParamsList()),
|
||||
provider.getForwardPayloadHeader(),
|
||||
provider.getForward()));
|
||||
}
|
||||
}
|
||||
log.info("auth rules resolve finish, RBAC rules size: {}, Jwt rules size: {}",
|
||||
allowAuthRules.size() + denyAuthRules.size(), jwtRules.size());
|
||||
applicationContext.publishEvent(new AuthDataChangedEvent(this,
|
||||
new AuthRules(allowAuthRules, denyAuthRules, jwtRules)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<RBAC> resolveRbac(List<HttpFilter> httpFilters) {
|
||||
return httpFilters.stream()
|
||||
.filter(httpFilter -> httpFilter.getName().equals(RBAC_FILTER))
|
||||
.map(httpFilter -> {
|
||||
try {
|
||||
return httpFilter.getTypedConfig().unpack(
|
||||
io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC.class);
|
||||
}
|
||||
catch (InvalidProtocolBufferException e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull)
|
||||
.map(io.envoyproxy.envoy.extensions.filters.http.rbac.v3.RBAC::getRules)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<HttpFilter> resolveHttpFilter(List<Listener> listeners) {
|
||||
return listeners.stream()
|
||||
.filter(listener -> listener.getName().equals(VIRTUAL_INBOUND))
|
||||
.map(Listener::getFilterChainsList)
|
||||
.flatMap(filterChains -> filterChains.stream()
|
||||
.map(FilterChain::getFiltersList))
|
||||
.flatMap(filters -> filters.stream()
|
||||
.filter(filter -> filter.getName().equals(CONNECTION_MANAGER)))
|
||||
.map(filter -> unpackHttpConnectionManager(filter.getTypedConfig()))
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(httpConnectionManager -> httpConnectionManager
|
||||
.getHttpFiltersList().stream())
|
||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<JwtAuthentication> resolveJWT(List<HttpFilter> httpFilters) {
|
||||
return httpFilters.stream()
|
||||
.filter(httpFilter -> httpFilter.getName().equals(JWT_FILTER))
|
||||
.map(httpFilter -> {
|
||||
try {
|
||||
return httpFilter.getTypedConfig()
|
||||
.unpack(JwtAuthentication.class);
|
||||
}
|
||||
catch (InvalidProtocolBufferException e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private HttpConnectionManager unpackHttpConnectionManager(Any any) {
|
||||
try {
|
||||
if (!any.is(HttpConnectionManager.class)) {
|
||||
return null;
|
||||
}
|
||||
return any.unpack(HttpConnectionManager.class);
|
||||
}
|
||||
catch (InvalidProtocolBufferException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AuthRule resolvePrincipal(Principal principal) {
|
||||
Principal.Set andIds = principal.getAndIds();
|
||||
AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND);
|
||||
for (Principal andId : andIds.getIdsList()) {
|
||||
if (andId.getAny()) {
|
||||
return null;
|
||||
}
|
||||
boolean isNot = false;
|
||||
if (andId.hasNotId()) {
|
||||
isNot = true;
|
||||
andId = andId.getNotId();
|
||||
}
|
||||
AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot);
|
||||
Principal.Set orIds = andId.getOrIds();
|
||||
for (Principal orId : orIds.getIdsList()) {
|
||||
if (orId.hasAuthenticated()
|
||||
&& orId.getAuthenticated().hasPrincipalName()) {
|
||||
StringMatcher identity = ConvUtil.convStringMatcher(
|
||||
orId.getAuthenticated().getPrincipalName());
|
||||
if (identity != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.IDENTITY, identity)));
|
||||
}
|
||||
}
|
||||
if (orId.hasDirectRemoteIp()) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.SOURCE_IP,
|
||||
ConvUtil.convertIpMatcher(orId.getDirectRemoteIp()))));
|
||||
}
|
||||
if (orId.hasRemoteIp()) {
|
||||
orChildren.addChildren(new AuthRule(
|
||||
new AuthCondition(AuthCondition.ValidationType.REMOTE_IP,
|
||||
ConvUtil.convertIpMatcher(orId.getRemoteIp()))));
|
||||
}
|
||||
if (orId.hasMetadata()
|
||||
&& ISTIO_AUTHN.equals(orId.getMetadata().getFilter())) {
|
||||
List<MetadataMatcher.PathSegment> segments = orId.getMetadata()
|
||||
.getPathList();
|
||||
switch (segments.get(0).getKey()) {
|
||||
case REQUEST_AUTH_PRINCIPAL:
|
||||
if (orId.hasMetadata() && orId.getMetadata().hasValue()
|
||||
&& orId.getMetadata().getValue().hasStringMatch()) {
|
||||
StringMatcher requestPrinciple = ConvUtil.convStringMatcher(
|
||||
orId.getMetadata().getValue().getStringMatch());
|
||||
if (requestPrinciple != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.REQUEST_PRINCIPALS,
|
||||
requestPrinciple)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REQUEST_AUTH_AUDIENCE:
|
||||
if (orId.hasMetadata() && orId.getMetadata().hasValue()
|
||||
&& orId.getMetadata().getValue().hasStringMatch()) {
|
||||
StringMatcher authAudience = ConvUtil.convStringMatcher(
|
||||
orId.getMetadata().getValue().getStringMatch());
|
||||
if (authAudience != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.AUTH_AUDIENCES,
|
||||
authAudience)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REQUEST_AUTH_PRESENTER:
|
||||
if (orId.hasMetadata() && orId.getMetadata().hasValue()
|
||||
&& orId.getMetadata().getValue().hasStringMatch()) {
|
||||
StringMatcher authPresenter = ConvUtil.convStringMatcher(
|
||||
orId.getMetadata().getValue().getStringMatch());
|
||||
if (authPresenter != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.AUTH_PRESENTERS,
|
||||
authPresenter)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REQUEST_AUTH_CLAIMS:
|
||||
if (orId.hasMetadata() && orId.getMetadata().hasValue()
|
||||
&& orId.getMetadata().getValue().hasListMatch()) {
|
||||
if (segments.size() >= 2) {
|
||||
String key = segments.get(1).getKey();
|
||||
StringMatcher stringMatcher = null;
|
||||
try {
|
||||
stringMatcher = ConvUtil.convStringMatcher(
|
||||
orId.getMetadata().getValue().getListMatch()
|
||||
.getOneOf().getStringMatch());
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(
|
||||
"unable to get/convert request auth claims");
|
||||
}
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.AUTH_CLAIMS, key,
|
||||
stringMatcher)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
if (orId.hasHeader()) {
|
||||
String headerName = orId.getHeader().getName();
|
||||
if (StringUtils.isEmpty(headerName)) {
|
||||
continue;
|
||||
}
|
||||
StringMatcher stringMatcher = ConvUtil
|
||||
.convertHeaderMatcher(orId.getHeader());
|
||||
orChildren.addChildren(new AuthRule(
|
||||
new AuthCondition(AuthCondition.ValidationType.HEADER,
|
||||
headerName, stringMatcher)));
|
||||
}
|
||||
}
|
||||
if (!orChildren.isEmpty()) {
|
||||
andChildren.addChildren(orChildren);
|
||||
}
|
||||
}
|
||||
return andChildren;
|
||||
}
|
||||
|
||||
private AuthRule resolvePermission(Permission permission) {
|
||||
Permission.Set andRules = permission.getAndRules();
|
||||
AuthRule andChildren = new AuthRule(AuthRule.RuleOperation.AND);
|
||||
for (Permission andRule : andRules.getRulesList()) {
|
||||
if (andRule.getAny()) {
|
||||
return null;
|
||||
}
|
||||
boolean isNot = false;
|
||||
if (andRule.hasNotRule()) {
|
||||
isNot = true;
|
||||
andRule = andRule.getNotRule();
|
||||
}
|
||||
Permission.Set orRules = andRule.getOrRules();
|
||||
AuthRule orChildren = new AuthRule(AuthRule.RuleOperation.OR, isNot);
|
||||
for (Permission orRule : orRules.getRulesList()) {
|
||||
int port = orRule.getDestinationPort();
|
||||
if (port > MIN_PORT && port <= MAX_PORT) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.PORTS, new PortMatcher(port))));
|
||||
}
|
||||
if (orRule.hasHeader()) {
|
||||
switch (orRule.getHeader().getName()) {
|
||||
case HEADER_NAME_AUTHORITY:
|
||||
StringMatcher host = ConvUtil
|
||||
.convStringMatcher(orRule.getHeader());
|
||||
if (host != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.HOSTS, host)));
|
||||
}
|
||||
break;
|
||||
case HEADER_NAME_METHOD:
|
||||
StringMatcher method = ConvUtil
|
||||
.convStringMatcher(orRule.getHeader());
|
||||
if (method != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.METHODS, method)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (orRule.hasUrlPath() && orRule.getUrlPath().hasPath()) {
|
||||
StringMatcher path = ConvUtil
|
||||
.convStringMatcher(orRule.getUrlPath().getPath());
|
||||
if (path != null) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.PATHS, path)));
|
||||
}
|
||||
}
|
||||
if (orRule.hasDestinationIp()) {
|
||||
orChildren.addChildren(new AuthRule(new AuthCondition(
|
||||
AuthCondition.ValidationType.DEST_IP,
|
||||
ConvUtil.convertIpMatcher(orRule.getDestinationIp()))));
|
||||
}
|
||||
}
|
||||
if (!orChildren.isEmpty()) {
|
||||
andChildren.addChildren(orChildren);
|
||||
}
|
||||
}
|
||||
return andChildren;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.LDS_URL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.filter.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.event.LabelRoutingDataChangedEvent;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcherType;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.filter.AbstractXdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.util.ConvUtil;
|
||||
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
import io.envoyproxy.envoy.config.route.v3.WeightedCluster;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class LabelRoutingXdsResolveFilter
|
||||
extends AbstractXdsResolveFilter<List<RouteConfiguration>> {
|
||||
|
||||
@Override
|
||||
public boolean resolve(List<RouteConfiguration> routeConfigurations) {
|
||||
if (routeConfigurations == null) {
|
||||
return false;
|
||||
}
|
||||
Map<String, UnifiedRouteDataStructure> untiedRouteDataStructures = new HashMap<>();
|
||||
for (RouteConfiguration routeConfiguration : routeConfigurations) {
|
||||
List<VirtualHost> virtualHosts = routeConfiguration.getVirtualHostsList();
|
||||
for (VirtualHost virtualHost : virtualHosts) {
|
||||
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
|
||||
String targetService = "";
|
||||
String[] serviceAndPort = virtualHost.getName().split(":");
|
||||
if (serviceAndPort.length > 0) {
|
||||
targetService = serviceAndPort[0].split("\\.")[0];
|
||||
}
|
||||
if (ALLOW_ANY.equals(targetService)) {
|
||||
continue;
|
||||
}
|
||||
unifiedRouteDataStructure.setTargetService(targetService);
|
||||
List<Route> routes = virtualHost.getRoutesList();
|
||||
LabelRouteRule labelRouteRule = getLabelRouteData(routes);
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule);
|
||||
untiedRouteDataStructures.put(
|
||||
unifiedRouteDataStructure.getTargetService(),
|
||||
unifiedRouteDataStructure);
|
||||
}
|
||||
}
|
||||
applicationContext.publishEvent(new LabelRoutingDataChangedEvent(this,
|
||||
untiedRouteDataStructures.values()));
|
||||
return true;
|
||||
}
|
||||
|
||||
private LabelRouteRule getLabelRouteData(List<Route> routes) {
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
LabelRouteRule labelRouteRule = new LabelRouteRule();
|
||||
for (Route route : routes) {
|
||||
String cluster = route.getRoute().getCluster();
|
||||
if (StringUtils.isNotEmpty(cluster)) {
|
||||
MatchService matchService = getMatchService(route, cluster, 100);
|
||||
matchServices.add(matchService);
|
||||
}
|
||||
WeightedCluster weightedCluster = route.getRoute().getWeightedClusters();
|
||||
for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster
|
||||
.getClustersList()) {
|
||||
MatchService matchService = getMatchService(route,
|
||||
clusterWeight.getName(), clusterWeight.getWeight().getValue());
|
||||
matchServices.add(matchService);
|
||||
}
|
||||
}
|
||||
labelRouteRule.setMatchRouteList(matchServices);
|
||||
if (!matchServices.isEmpty()) {
|
||||
labelRouteRule.setDefaultRouteVersion(
|
||||
matchServices.get(matchServices.size() - 1).getVersion());
|
||||
}
|
||||
return labelRouteRule;
|
||||
}
|
||||
|
||||
private MatchService getMatchService(Route route, String cluster, int weight) {
|
||||
String version = "";
|
||||
try {
|
||||
String[] info = cluster.split("\\|");
|
||||
version = info[2];
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("invalid cluster info for route {}", route.getName());
|
||||
}
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion(version);
|
||||
matchService.setRuleList(match2RouteRules(route.getMatch()));
|
||||
matchService.setWeight(weight);
|
||||
return matchService;
|
||||
}
|
||||
|
||||
private List<RouteRule> match2RouteRules(RouteMatch routeMatch) {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) {
|
||||
HeaderRule headerRule = ConvUtil.headerMatcher2HeaderRule(headerMatcher);
|
||||
if (headerRule != null) {
|
||||
routeRules.add(headerRule);
|
||||
}
|
||||
}
|
||||
|
||||
for (QueryParameterMatcher parameterMatcher : routeMatch
|
||||
.getQueryParametersList()) {
|
||||
UrlRule.Parameter parameter = ConvUtil
|
||||
.parameterMatcher2ParameterRule(parameterMatcher);
|
||||
if (parameter != null) {
|
||||
routeRules.add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
UrlRule.Path path = new UrlRule.Path();
|
||||
path.setType(PATH);
|
||||
switch (routeMatch.getPathSpecifierCase()) {
|
||||
case PREFIX:
|
||||
path.setCondition(StringMatcherType.PREFIX.toString());
|
||||
path.setValue(routeMatch.getPrefix());
|
||||
break;
|
||||
|
||||
case PATH:
|
||||
path.setCondition(StringMatcherType.EXACT.toString());
|
||||
path.setValue(routeMatch.getPath());
|
||||
break;
|
||||
|
||||
case SAFE_REGEX:
|
||||
path.setCondition(StringMatcherType.REGEX.toString());
|
||||
path.setValue(routeMatch.getSafeRegex().getRegex());
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown type
|
||||
path = null;
|
||||
|
||||
}
|
||||
if (path != null) {
|
||||
routeRules.add(path);
|
||||
}
|
||||
return routeRules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.RDS_URL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.NodeBuilder;
|
||||
import com.alibaba.cloud.governance.istio.XdsChannel;
|
||||
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
|
||||
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
|
||||
import io.envoyproxy.envoy.config.core.v3.Node;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryRequest;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public abstract class AbstractXdsProtocol<T>
|
||||
implements XdsProtocol<T>, ApplicationContextAware {
|
||||
|
||||
protected static final Logger log = LoggerFactory
|
||||
.getLogger(AbstractXdsProtocol.class);
|
||||
|
||||
protected XdsChannel xdsChannel;
|
||||
|
||||
protected final Node node = NodeBuilder.getNode();
|
||||
|
||||
protected XdsConfigProperties xdsConfigProperties;
|
||||
|
||||
protected List<XdsResolveFilter<List<T>>> filters = new ArrayList<>();
|
||||
|
||||
private Set<String> resourceNames = new HashSet<>();
|
||||
|
||||
private final XdsScheduledThreadPool xdsScheduledThreadPool;
|
||||
|
||||
/**
|
||||
* does the protocol need polling.
|
||||
*/
|
||||
private boolean needPolling;
|
||||
|
||||
/**
|
||||
* send event to submodules.
|
||||
*/
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
private final Map<Long, StreamObserver<DiscoveryRequest>> requestObserverMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Long, CompletableFuture<List<T>>> futureMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<Long, Set<String>> requestResource = new ConcurrentHashMap<>();
|
||||
|
||||
protected final static AtomicLong requestId = new AtomicLong(0);
|
||||
|
||||
public AbstractXdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
XdsConfigProperties xdsConfigProperties) {
|
||||
this.xdsChannel = xdsChannel;
|
||||
this.xdsScheduledThreadPool = xdsScheduledThreadPool;
|
||||
this.xdsConfigProperties = xdsConfigProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void setNeedPolling(boolean needPolling) {
|
||||
this.needPolling = needPolling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long observeResource(Set<String> resourceNames,
|
||||
Consumer<List<T>> consumer) {
|
||||
long id = getDefaultRequestId();
|
||||
if (resourceNames == null) {
|
||||
resourceNames = new HashSet<>();
|
||||
}
|
||||
requestResource.put(id, resourceNames);
|
||||
try {
|
||||
consumer.accept(doGetResource(id, resourceNames, consumer));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error on get observe resource from xds", e);
|
||||
}
|
||||
if (needPolling) {
|
||||
xdsScheduledThreadPool.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
consumer.accept(doGetResource(id, requestResource.get(id), consumer));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error on get observe resource from xds", e);
|
||||
}
|
||||
}, xdsConfigProperties.getPollingTime(), xdsConfigProperties.getPollingTime(),
|
||||
TimeUnit.SECONDS);
|
||||
needPolling = false;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getResource(Set<String> resourceNames) {
|
||||
long id = requestId.getAndDecrement();
|
||||
List<T> source = doGetResource(id, resourceNames, null);
|
||||
requestObserverMap.remove(id);
|
||||
return source;
|
||||
}
|
||||
|
||||
public Set<String> getResourceNames() {
|
||||
return resourceNames;
|
||||
}
|
||||
|
||||
private List<T> doGetResource(long id, Set<String> resourceNames,
|
||||
Consumer<List<T>> consumer) {
|
||||
if (resourceNames == null) {
|
||||
resourceNames = new HashSet<>();
|
||||
}
|
||||
CompletableFuture<List<T>> future = new CompletableFuture<>();
|
||||
futureMap.put(id, future);
|
||||
StreamObserver<DiscoveryRequest> requestObserver = requestObserverMap.get(id);
|
||||
if (requestObserver == null) {
|
||||
// reuse observer
|
||||
requestObserver = xdsChannel
|
||||
.createDiscoveryRequest(new XdsObserver(id, consumer));
|
||||
// requestObserver may be null when testing
|
||||
if (requestObserver != null) {
|
||||
requestObserverMap.put(id, requestObserver);
|
||||
}
|
||||
}
|
||||
sendXdsRequest(requestObserver, resourceNames);
|
||||
try {
|
||||
return future.get();
|
||||
}
|
||||
catch (ExecutionException | InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
futureMap.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<T> decodeXdsResponse(DiscoveryResponse response);
|
||||
|
||||
protected Set<String> resolveResourceNames(List<T> resources) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
protected void fireXdsFilters(List<T> resources) {
|
||||
try {
|
||||
this.resourceNames = resolveResourceNames(resources);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error on resolving resource names from {}", resources);
|
||||
}
|
||||
for (XdsResolveFilter<List<T>> filter : filters) {
|
||||
try {
|
||||
if (!filter.resolve(resources)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Error on executing Xds filter {}", filter.getClass().getName(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendXdsRequest(StreamObserver<DiscoveryRequest> observer,
|
||||
Set<String> resourceNames) {
|
||||
DiscoveryRequest request = DiscoveryRequest.newBuilder().setNode(node)
|
||||
.setTypeUrl(getTypeUrl()).addAllResourceNames(resourceNames).build();
|
||||
observer.onNext(request);
|
||||
}
|
||||
|
||||
private void sendAckRequest(long id, DiscoveryResponse response) {
|
||||
StreamObserver<DiscoveryRequest> observer = requestObserverMap.get(id);
|
||||
if (observer == null) {
|
||||
return;
|
||||
}
|
||||
DiscoveryRequest request = DiscoveryRequest.newBuilder()
|
||||
.setVersionInfo(response.getVersionInfo()).setNode(node)
|
||||
.addAllResourceNames(requestResource.get(id) == null ? new ArrayList<>()
|
||||
: requestResource.get(id))
|
||||
.setTypeUrl(response.getTypeUrl()).setResponseNonce(response.getNonce())
|
||||
.build();
|
||||
observer.onNext(request);
|
||||
}
|
||||
|
||||
private int getDefaultRequestId() {
|
||||
switch (getTypeUrl()) {
|
||||
case IstioConstants.CDS_URL:
|
||||
return -1;
|
||||
case IstioConstants.EDS_URL:
|
||||
return -2;
|
||||
case IstioConstants.LDS_URL:
|
||||
return -3;
|
||||
case IstioConstants.RDS_URL:
|
||||
return -4;
|
||||
}
|
||||
throw new UnsupportedOperationException("Unknown type url");
|
||||
}
|
||||
|
||||
private class XdsObserver implements StreamObserver<DiscoveryResponse> {
|
||||
|
||||
private Consumer<List<T>> consumer;
|
||||
|
||||
private long id;
|
||||
|
||||
XdsObserver(long id, Consumer<List<T>> consumer) {
|
||||
this.id = id;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(DiscoveryResponse discoveryResponse) {
|
||||
if (xdsChannel == null) {
|
||||
return;
|
||||
}
|
||||
if (xdsConfigProperties.isLogXds()) {
|
||||
log.info("receive notification from xds server, type: " + getTypeUrl()
|
||||
+ " requestId: " + id);
|
||||
}
|
||||
List<T> responses = decodeXdsResponse(discoveryResponse);
|
||||
CompletableFuture<List<T>> future = futureMap.get(id);
|
||||
if (future == null) {
|
||||
// means it is push operation from xds, consume it directly
|
||||
consumer.accept(responses);
|
||||
sendAckRequest(id, discoveryResponse);
|
||||
return;
|
||||
}
|
||||
future.complete(responses);
|
||||
sendAckRequest(id, discoveryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable throwable) {
|
||||
if (xdsChannel == null) {
|
||||
return;
|
||||
}
|
||||
if (xdsConfigProperties.isLogXds()) {
|
||||
log.error("connect to xds server failed, reconnecting", throwable);
|
||||
}
|
||||
CompletableFuture<List<T>> future = futureMap.get(id);
|
||||
if (future != null) {
|
||||
future.complete(null);
|
||||
futureMap.remove(id);
|
||||
}
|
||||
requestResource.remove(id);
|
||||
// refresh token again
|
||||
xdsChannel.refreshIstiodToken();
|
||||
// reconnected immediately
|
||||
StreamObserver<DiscoveryRequest> observer = xdsChannel
|
||||
.createDiscoveryRequest(new XdsObserver(id, consumer));
|
||||
if (observer != null) {
|
||||
requestObserverMap.put(id, observer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
log.info("xds connect completed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public interface XdsProtocol<T> {
|
||||
|
||||
List<T> getResource(Set<String> resourceNames);
|
||||
|
||||
String getTypeUrl();
|
||||
|
||||
long observeResource(Set<String> resourceNames, Consumer<List<T>> consumer);
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.XdsChannel;
|
||||
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
|
||||
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
|
||||
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> CdsProtocol contains infomation about
|
||||
* service.
|
||||
*/
|
||||
public class CdsProtocol extends AbstractXdsProtocol<Cluster> {
|
||||
|
||||
public CdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
XdsConfigProperties xdsConfigProperties) {
|
||||
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Cluster> decodeXdsResponse(DiscoveryResponse response) {
|
||||
List<Cluster> clusters = new ArrayList<>();
|
||||
for (com.google.protobuf.Any res : response.getResourcesList()) {
|
||||
try {
|
||||
Cluster cluster = res.unpack(Cluster.class);
|
||||
clusters.add(cluster);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("unpack cluster failed", e);
|
||||
}
|
||||
}
|
||||
fireXdsFilters(clusters);
|
||||
return clusters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> resolveResourceNames(List<Cluster> resources) {
|
||||
Set<String> endpoints = new HashSet<>();
|
||||
if (resources == null) {
|
||||
return endpoints;
|
||||
}
|
||||
for (Cluster cluster : resources) {
|
||||
cluster.getEdsClusterConfig().getServiceName();
|
||||
endpoints.add(cluster.getEdsClusterConfig().getServiceName());
|
||||
}
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.CDS_URL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.XdsChannel;
|
||||
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
|
||||
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
|
||||
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> TODO: Fetch all endpoints in EdsProtocol.
|
||||
*/
|
||||
public class EdsProtocol extends AbstractXdsProtocol<ClusterLoadAssignment> {
|
||||
|
||||
public EdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
XdsConfigProperties xdsConfigProperties) {
|
||||
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ClusterLoadAssignment> decodeXdsResponse(DiscoveryResponse response) {
|
||||
List<ClusterLoadAssignment> endpoints = new ArrayList<>();
|
||||
for (com.google.protobuf.Any res : response.getResourcesList()) {
|
||||
try {
|
||||
ClusterLoadAssignment endpoint = res.unpack(ClusterLoadAssignment.class);
|
||||
endpoints.add(endpoint);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("unpack cluster failed", e);
|
||||
}
|
||||
}
|
||||
fireXdsFilters(endpoints);
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.EDS_URL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.governance.istio.XdsChannel;
|
||||
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
|
||||
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Filter;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager;
|
||||
import io.envoyproxy.envoy.extensions.filters.network.http_connection_manager.v3.Rds;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> LdsProtocol contains the authentication
|
||||
* configuration and other configuration about security.
|
||||
*/
|
||||
public class LdsProtocol extends AbstractXdsProtocol<Listener> {
|
||||
|
||||
public LdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
XdsConfigProperties xdsConfigProperties,
|
||||
List<XdsResolveFilter<List<Listener>>> ldsFilters) {
|
||||
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
// init filters
|
||||
for (XdsResolveFilter<List<Listener>> filter : ldsFilters) {
|
||||
if (IstioConstants.LDS_URL.equals(filter.getTypeUrl())) {
|
||||
filters.add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.LDS_URL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Listener> decodeXdsResponse(DiscoveryResponse response) {
|
||||
List<Listener> listeners = new ArrayList<>();
|
||||
for (com.google.protobuf.Any res : response.getResourcesList()) {
|
||||
try {
|
||||
Listener listener = res.unpack(Listener.class);
|
||||
if (listener != null) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("unpack listeners failed", e);
|
||||
}
|
||||
}
|
||||
fireXdsFilters(listeners);
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> resolveResourceNames(List<Listener> resources) {
|
||||
Set<String> routeNames = new HashSet<>();
|
||||
resources.forEach(listener -> routeNames.addAll(listener.getFilterChainsList()
|
||||
.stream().flatMap((e) -> e.getFiltersList().stream())
|
||||
.map(Filter::getTypedConfig).map(any -> {
|
||||
try {
|
||||
if (!any.is(HttpConnectionManager.class)) {
|
||||
return null;
|
||||
}
|
||||
return any.unpack(HttpConnectionManager.class);
|
||||
}
|
||||
catch (InvalidProtocolBufferException e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).map(HttpConnectionManager::getRds)
|
||||
.map(Rds::getRouteConfigName).filter(StringUtils::isNotEmpty)
|
||||
.collect(Collectors.toList())));
|
||||
return routeNames;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.protocol.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.governance.istio.XdsChannel;
|
||||
import com.alibaba.cloud.governance.istio.XdsConfigProperties;
|
||||
import com.alibaba.cloud.governance.istio.XdsScheduledThreadPool;
|
||||
import com.alibaba.cloud.governance.istio.constant.IstioConstants;
|
||||
import com.alibaba.cloud.governance.istio.filter.XdsResolveFilter;
|
||||
import com.alibaba.cloud.governance.istio.protocol.AbstractXdsProtocol;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> RdsProtocol contains route info.
|
||||
*/
|
||||
public class RdsProtocol extends AbstractXdsProtocol<RouteConfiguration> {
|
||||
|
||||
public RdsProtocol(XdsChannel xdsChannel,
|
||||
XdsScheduledThreadPool xdsScheduledThreadPool,
|
||||
XdsConfigProperties xdsConfigProperties,
|
||||
List<XdsResolveFilter<List<RouteConfiguration>>> rdsFilters) {
|
||||
super(xdsChannel, xdsScheduledThreadPool, xdsConfigProperties);
|
||||
for (XdsResolveFilter<List<RouteConfiguration>> filter : rdsFilters) {
|
||||
if (IstioConstants.RDS_URL.equals(filter.getTypeUrl())) {
|
||||
filters.add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RouteConfiguration> decodeXdsResponse(DiscoveryResponse response) {
|
||||
List<RouteConfiguration> routes = new ArrayList<>();
|
||||
for (com.google.protobuf.Any res : response.getResourcesList()) {
|
||||
try {
|
||||
RouteConfiguration route = res.unpack(RouteConfiguration.class);
|
||||
routes.add(route);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("unpack cluster failed", e);
|
||||
}
|
||||
}
|
||||
fireXdsFilters(routes);
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeUrl() {
|
||||
return IstioConstants.RDS_URL;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio.util;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.commons.matcher.IpMatcher;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcher;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcherType;
|
||||
import io.envoyproxy.envoy.config.core.v3.CidrRange;
|
||||
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public final class ConvUtil {
|
||||
|
||||
private static final String HEADER = "header";
|
||||
|
||||
private static final String PARAMETER = "parameter";
|
||||
|
||||
private ConvUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static StringMatcher convStringMatcher(
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) {
|
||||
if (stringMatcher == null) {
|
||||
return null;
|
||||
}
|
||||
boolean isIgnoreCase = stringMatcher.getIgnoreCase();
|
||||
String exact = stringMatcher.getExact();
|
||||
String prefix = stringMatcher.getPrefix();
|
||||
String suffix = stringMatcher.getSuffix();
|
||||
String contains = stringMatcher.getContains();
|
||||
String regex = stringMatcher.getSafeRegex().getRegex();
|
||||
if (StringUtils.isNotBlank(exact)) {
|
||||
return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(prefix)) {
|
||||
return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(suffix)) {
|
||||
return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(contains)) {
|
||||
return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(regex)) {
|
||||
return new StringMatcher(regex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static StringMatcher convStringMatcher(
|
||||
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
|
||||
return convStringMatcher(headerMatch2StringMatch(headerMatcher));
|
||||
}
|
||||
|
||||
public static IpMatcher convertIpMatcher(CidrRange cidrRange) {
|
||||
return new IpMatcher(cidrRange.getPrefixLen().getValue(),
|
||||
cidrRange.getAddressPrefix());
|
||||
}
|
||||
|
||||
public static StringMatcher convertHeaderMatcher(
|
||||
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
|
||||
return convStringMatcher(headerMatch2StringMatch(headerMatcher));
|
||||
}
|
||||
|
||||
public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch(
|
||||
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
|
||||
if (headerMatcher == null) {
|
||||
return null;
|
||||
}
|
||||
if (headerMatcher.getPresentMatch()) {
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
|
||||
.newBuilder();
|
||||
return builder.setSafeRegex(RegexMatcher.newBuilder().build())
|
||||
.setIgnoreCase(true).build();
|
||||
}
|
||||
if (!headerMatcher.hasStringMatch()) {
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
|
||||
.newBuilder();
|
||||
String exactMatch = headerMatcher.getExactMatch();
|
||||
String containsMatch = headerMatcher.getContainsMatch();
|
||||
String prefixMatch = headerMatcher.getPrefixMatch();
|
||||
String suffixMatch = headerMatcher.getSuffixMatch();
|
||||
RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch();
|
||||
if (!StringUtils.isEmpty(exactMatch)) {
|
||||
builder.setExact(exactMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(containsMatch)) {
|
||||
builder.setContains(containsMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(prefixMatch)) {
|
||||
builder.setPrefix(prefixMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(suffixMatch)) {
|
||||
builder.setSuffix(suffixMatch);
|
||||
}
|
||||
else if (safeRegex.isInitialized()) {
|
||||
builder.setSafeRegex(safeRegex);
|
||||
}
|
||||
return builder.setIgnoreCase(true).build();
|
||||
}
|
||||
return headerMatcher.getStringMatch();
|
||||
}
|
||||
|
||||
public static UrlRule.Parameter parameterMatcher2ParameterRule(
|
||||
QueryParameterMatcher queryParameterMatcher) {
|
||||
UrlRule.Parameter parameter = new UrlRule.Parameter();
|
||||
StringMatcher stringMatcher = ConvUtil
|
||||
.convStringMatcher(queryParameterMatcher.getStringMatch());
|
||||
if (stringMatcher != null) {
|
||||
parameter.setCondition(stringMatcher.getType().toString());
|
||||
parameter.setKey(queryParameterMatcher.getName());
|
||||
parameter.setValue(stringMatcher.getMatcher());
|
||||
parameter.setType(PARAMETER);
|
||||
return parameter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) {
|
||||
StringMatcher stringMatcher = ConvUtil
|
||||
.convStringMatcher(ConvUtil.headerMatch2StringMatch(headerMatcher));
|
||||
if (stringMatcher != null) {
|
||||
HeaderRule headerRule = new HeaderRule();
|
||||
headerRule.setCondition(stringMatcher.getType().toString());
|
||||
headerRule.setKey(headerMatcher.getName());
|
||||
headerRule.setValue(stringMatcher.getMatcher());
|
||||
headerRule.setType(HEADER);
|
||||
return headerRule;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.alibaba.cloud.governance.istio.XdsAutoConfiguration
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.istio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.commons.io.FileUtils;
|
||||
import com.alibaba.cloud.governance.auth.AuthenticationAutoConfiguration;
|
||||
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.LdsProtocol;
|
||||
import com.alibaba.cloud.governance.istio.protocol.impl.RdsProtocol;
|
||||
import com.alibaba.cloud.router.LabelRoutingAutoConfiguration;
|
||||
import com.alibaba.cloud.router.repository.FilterService;
|
||||
import com.alibaba.cloud.router.repository.RouteDataRepository;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.envoyproxy.envoy.config.listener.v3.Listener;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.service.discovery.v3.DiscoveryResponse;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = XdsRulesTests.TestConfig.class,
|
||||
properties = { "spring.cloud.istio.config.port=15010",
|
||||
"spring.cloud.istio.config.enabled=true",
|
||||
"spring.cloud.istio.config.log-xds=false",
|
||||
"spring.cloud.nacos.discovery.watch.enabled=false" },
|
||||
webEnvironment = NONE)
|
||||
@EnableFeignClients
|
||||
public class XdsRulesTests {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(XdsRulesTests.class);
|
||||
|
||||
private static final String TARGET_SERVICE = "service-provider";
|
||||
|
||||
@Autowired
|
||||
private AuthRepository authRepository;
|
||||
|
||||
@Autowired
|
||||
private RouteDataRepository routeDataRepository;
|
||||
|
||||
@Autowired
|
||||
private LdsProtocol ldsProtocol;
|
||||
|
||||
@Autowired
|
||||
private RdsProtocol rdsProtocol;
|
||||
|
||||
private DiscoveryResponse decodeResponse(String path) throws Exception {
|
||||
File file = new File(path);
|
||||
FileInputStream stream = FileUtils.openInputStream(file);
|
||||
byte[] bytes = new byte[(int) file.length()];
|
||||
int readBytes = stream.read(bytes);
|
||||
if (readBytes == -1) {
|
||||
throw new Exception("Unreadable response file");
|
||||
}
|
||||
return DiscoveryResponse.parseFrom(bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthTransform() throws Exception {
|
||||
DiscoveryResponse discoveryResponse = decodeResponse(
|
||||
"src/test/resources/LdsResponse.in");
|
||||
List<Listener> listeners = ldsProtocol.decodeXdsResponse(discoveryResponse);
|
||||
if (listeners == null) {
|
||||
throw new Exception("Can not parse listeners from xds response");
|
||||
}
|
||||
log.info("Auth rules are {}", JSONObject.toJSONString(authRepository));
|
||||
Assert.assertEquals(authRepository.getAllowAuthRules().size(), 1);
|
||||
Assert.assertEquals(authRepository.getDenyAuthRules().size(), 1);
|
||||
Assert.assertEquals(authRepository.getJwtRules().size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelRoutingTransform() throws Exception {
|
||||
DiscoveryResponse discoveryResponse = decodeResponse(
|
||||
"src/test/resources/RdsResponse.in");
|
||||
List<RouteConfiguration> routeConfigurations = rdsProtocol
|
||||
.decodeXdsResponse(discoveryResponse);
|
||||
if (routeConfigurations == null) {
|
||||
throw new Exception("Can not parse route configurations from xds response");
|
||||
}
|
||||
if (routeDataRepository.getRouteRule(TARGET_SERVICE) == null) {
|
||||
throw new Exception("Can not get target service from route configurations");
|
||||
}
|
||||
log.info("Label routing rules are {}", JSONObject
|
||||
.toJSONString(routeDataRepository.getRouteRule(TARGET_SERVICE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* dummy class for label routing filter service.
|
||||
*/
|
||||
static class Dummy {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ImportAutoConfiguration({ XdsAutoConfiguration.class,
|
||||
AuthenticationAutoConfiguration.class, LabelRoutingAutoConfiguration.class })
|
||||
public static class TestConfig {
|
||||
|
||||
@Bean(name = TARGET_SERVICE + FilterService.FEIGN_CLIENT_BEAN_SPECIFICATION)
|
||||
public Dummy dummy() {
|
||||
return new Dummy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-starters</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>spring-cloud-starter-alibaba-controlplane-opensergo</artifactId>
|
||||
<name>Spring Cloud Alibaba OpenSergo Control Plane</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opensergo</groupId>
|
||||
<artifactId>opensergo-java-sdk</artifactId>
|
||||
<version>${opensergo.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Only for unit test-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.16</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.opensergo.config.enabled",
|
||||
matchIfMissing = true)
|
||||
@EnableConfigurationProperties(OpenSergoConfigProperties.class)
|
||||
@AutoConfigureOrder(OpenSergoAutoConfig.OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER)
|
||||
public class OpenSergoAutoConfig {
|
||||
|
||||
/**
|
||||
* Order of OpenSergo auto config.
|
||||
*/
|
||||
public static final int OPENSERGO_RESOURCE_AUTO_CONFIG_ORDER = 101;
|
||||
|
||||
@Autowired
|
||||
private OpenSergoConfigProperties openSergoConfigProperties;
|
||||
|
||||
@Bean
|
||||
public OpenSergoTrafficRouterParser openSergoTrafficRouterParser() {
|
||||
return new OpenSergoTrafficRouterParser();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenSergoTrafficExchanger openSergoTrafficExchanger(
|
||||
OpenSergoTrafficRouterParser openSergoTrafficRouterParser) {
|
||||
return new OpenSergoTrafficExchanger(openSergoConfigProperties,
|
||||
openSergoTrafficRouterParser);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TargetServiceChangedListener targetServiceChangedListener(
|
||||
OpenSergoTrafficExchanger openSergoTrafficExchanger) {
|
||||
return new TargetServiceChangedListener(openSergoConfigProperties,
|
||||
openSergoTrafficExchanger);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
@ConfigurationProperties(OpenSergoConfigProperties.PREFIX)
|
||||
public class OpenSergoConfigProperties {
|
||||
|
||||
/**
|
||||
* Prefix in yaml.
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.opensergo";
|
||||
|
||||
/**
|
||||
* Configurations about OpenSergo Server Endpoint.
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* Namespace Configuration about OpenSergo Config.
|
||||
*/
|
||||
private String namespace = "default";
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.LabelRouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.MatchService;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.HeaderRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.RouteRule;
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.rule.UrlRule;
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcher;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcherType;
|
||||
import com.alibaba.cloud.governance.opensergo.util.ConvUtils;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
|
||||
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.QueryParameterMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
import io.envoyproxy.envoy.config.route.v3.WeightedCluster;
|
||||
import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
public class OpenSergoTrafficRouterParser {
|
||||
|
||||
protected static final Logger log = LoggerFactory
|
||||
.getLogger(OpenSergoTrafficRouterParser.class);
|
||||
|
||||
private static final String HEADER = "header";
|
||||
|
||||
private static final String PARAMETER = "parameter";
|
||||
|
||||
private static final String PATH = "path";
|
||||
|
||||
public OpenSergoTrafficRouterParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* transform rds RouterConfig list to spring cloud alibaba router data list.
|
||||
* @param routeConfigurations the routerConfig list from OpenSergo control plane.
|
||||
* @return spring cloud alibaba router rules.
|
||||
* @throws InvalidProtocolBufferException transform exception.
|
||||
*/
|
||||
public Collection<UnifiedRouteDataStructure> resolveLabelRouting(
|
||||
List<RouteConfiguration> routeConfigurations)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (routeConfigurations == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Map<String, UnifiedRouteDataStructure> unifiedRouteDataStructures = new HashMap<>();
|
||||
for (RouteConfiguration routeConfiguration : routeConfigurations) {
|
||||
List<VirtualHost> virtualHosts = routeConfiguration.getVirtualHostsList();
|
||||
for (VirtualHost virtualHost : virtualHosts) {
|
||||
UnifiedRouteDataStructure unifiedRouteDataStructure = new UnifiedRouteDataStructure();
|
||||
String targetService = "";
|
||||
String[] serviceAndPort = virtualHost.getName().split(":");
|
||||
if (serviceAndPort.length > 0) {
|
||||
targetService = serviceAndPort[0].split("\\.")[0];
|
||||
}
|
||||
unifiedRouteDataStructure.setTargetService(targetService);
|
||||
List<Route> routes = virtualHost.getRoutesList();
|
||||
LabelRouteRule labelRouteRule = getLabelRouteData(routes);
|
||||
unifiedRouteDataStructure.setLabelRouteRule(labelRouteRule);
|
||||
unifiedRouteDataStructures.put(
|
||||
unifiedRouteDataStructure.getTargetService(),
|
||||
unifiedRouteDataStructure);
|
||||
}
|
||||
}
|
||||
return unifiedRouteDataStructures.values();
|
||||
}
|
||||
|
||||
private LabelRouteRule getLabelRouteData(List<Route> routes)
|
||||
throws InvalidProtocolBufferException {
|
||||
List<MatchService> matchServices = new ArrayList<>();
|
||||
LabelRouteRule labelRouteRule = new LabelRouteRule();
|
||||
for (Route route : routes) {
|
||||
ClusterSpecifierPlugin clusterSpecifierPlugin = route.getRoute()
|
||||
.getInlineClusterSpecifierPlugin();
|
||||
String cluster = "";
|
||||
String fallbackCluster = "";
|
||||
if (clusterSpecifierPlugin != null) {
|
||||
ClusterFallbackConfig_ClusterConfig fallbackConfig = ConvUtils
|
||||
.convFallbackClusterConfig(clusterSpecifierPlugin);
|
||||
fallbackCluster = fallbackConfig.getFallbackCluster();
|
||||
cluster = fallbackConfig.getRoutingCluster();
|
||||
}
|
||||
if (StringUtils.isEmpty(cluster)) {
|
||||
cluster = route.getRoute().getCluster();
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(cluster)) {
|
||||
MatchService matchService = null;
|
||||
if (StringUtils.isNotEmpty(fallbackCluster)) {
|
||||
matchService = getMatchService(route, cluster, 100,
|
||||
getVersion(route, fallbackCluster));
|
||||
}
|
||||
else {
|
||||
matchService = getMatchService(route, cluster, 100);
|
||||
}
|
||||
|
||||
matchServices.add(matchService);
|
||||
}
|
||||
|
||||
WeightedCluster weightedCluster = route.getRoute().getWeightedClusters();
|
||||
for (WeightedCluster.ClusterWeight clusterWeight : weightedCluster
|
||||
.getClustersList()) {
|
||||
MatchService matchService = getMatchService(route,
|
||||
clusterWeight.getName(), clusterWeight.getWeight().getValue());
|
||||
matchServices.add(matchService);
|
||||
}
|
||||
}
|
||||
labelRouteRule.setMatchRouteList(matchServices);
|
||||
if (!matchServices.isEmpty()) {
|
||||
labelRouteRule.setDefaultRouteVersion(
|
||||
matchServices.get(matchServices.size() - 1).getVersion());
|
||||
}
|
||||
return labelRouteRule;
|
||||
}
|
||||
|
||||
private MatchService getMatchService(Route route, String cluster, int weight) {
|
||||
return getMatchService(route, cluster, weight, null);
|
||||
}
|
||||
|
||||
private MatchService getMatchService(Route route, String cluster, int weight,
|
||||
String fallback) {
|
||||
String version = getVersion(route, cluster);
|
||||
MatchService matchService = new MatchService();
|
||||
matchService.setVersion(version);
|
||||
matchService.setRuleList(match2RouteRules(route.getMatch()));
|
||||
matchService.setWeight(weight);
|
||||
if (StringUtils.isNotEmpty(fallback)) {
|
||||
matchService.setFallback(fallback);
|
||||
}
|
||||
return matchService;
|
||||
}
|
||||
|
||||
private String getVersion(Route route, String cluster) {
|
||||
String version = "";
|
||||
try {
|
||||
String[] info = cluster.split("\\|");
|
||||
version = info[2];
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("invalid cluster info for route {}", route.getName());
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private List<RouteRule> match2RouteRules(RouteMatch routeMatch) {
|
||||
List<RouteRule> routeRules = new ArrayList<>();
|
||||
for (HeaderMatcher headerMatcher : routeMatch.getHeadersList()) {
|
||||
HeaderRule headerRule = headerMatcher2HeaderRule(headerMatcher);
|
||||
if (headerRule != null) {
|
||||
routeRules.add(headerRule);
|
||||
}
|
||||
}
|
||||
|
||||
for (QueryParameterMatcher parameterMatcher : routeMatch
|
||||
.getQueryParametersList()) {
|
||||
UrlRule.Parameter parameter = parameterMatcher2ParameterRule(
|
||||
parameterMatcher);
|
||||
if (parameter != null) {
|
||||
routeRules.add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
UrlRule.Path path = new UrlRule.Path();
|
||||
path.setType(PATH);
|
||||
switch (routeMatch.getPathSpecifierCase()) {
|
||||
case PREFIX:
|
||||
path.setCondition(StringMatcherType.PREFIX.toString());
|
||||
path.setValue(routeMatch.getPrefix());
|
||||
break;
|
||||
|
||||
case PATH:
|
||||
path.setCondition(StringMatcherType.EXACT.toString());
|
||||
path.setValue(routeMatch.getPath());
|
||||
break;
|
||||
|
||||
case SAFE_REGEX:
|
||||
path.setCondition(StringMatcherType.REGEX.toString());
|
||||
path.setValue(routeMatch.getSafeRegex().getRegex());
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown type
|
||||
path = null;
|
||||
|
||||
}
|
||||
if (path != null) {
|
||||
routeRules.add(path);
|
||||
}
|
||||
return routeRules;
|
||||
}
|
||||
|
||||
private UrlRule.Parameter parameterMatcher2ParameterRule(
|
||||
QueryParameterMatcher queryParameterMatcher) {
|
||||
UrlRule.Parameter parameter = new UrlRule.Parameter();
|
||||
StringMatcher stringMatcher = ConvUtils
|
||||
.convStringMatcher(queryParameterMatcher.getStringMatch());
|
||||
if (stringMatcher != null) {
|
||||
parameter.setCondition(stringMatcher.getType().toString());
|
||||
parameter.setKey(queryParameterMatcher.getName());
|
||||
parameter.setValue(stringMatcher.getMatcher());
|
||||
parameter.setType(PARAMETER);
|
||||
return parameter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private HeaderRule headerMatcher2HeaderRule(HeaderMatcher headerMatcher) {
|
||||
StringMatcher stringMatcher = ConvUtils
|
||||
.convStringMatcher(ConvUtils.headerMatch2StringMatch(headerMatcher));
|
||||
if (stringMatcher != null) {
|
||||
HeaderRule headerRule = new HeaderRule();
|
||||
headerRule.setCondition(stringMatcher.getType().toString());
|
||||
headerRule.setKey(headerMatcher.getName());
|
||||
headerRule.setValue(stringMatcher.getMatcher());
|
||||
headerRule.setType(HEADER);
|
||||
return headerRule;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.event.TargetServiceChangedEvent;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
/**
|
||||
* Subscribe OpenSergo configuration when provider service changed.
|
||||
*
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
public class TargetServiceChangedListener
|
||||
implements ApplicationListener<TargetServiceChangedEvent> {
|
||||
|
||||
private OpenSergoTrafficExchanger openSergoTrafficExchanger;
|
||||
|
||||
private OpenSergoConfigProperties openSergoConfigProperties;
|
||||
|
||||
public TargetServiceChangedListener(
|
||||
OpenSergoConfigProperties openSergoConfigProperties,
|
||||
OpenSergoTrafficExchanger openSergoTrafficExchanger) {
|
||||
this.openSergoConfigProperties = openSergoConfigProperties;
|
||||
this.openSergoTrafficExchanger = openSergoTrafficExchanger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(TargetServiceChangedEvent targetServiceChangedEvent) {
|
||||
Object source = targetServiceChangedEvent.getSource();
|
||||
if (source instanceof String) {
|
||||
String targetService = (String) targetServiceChangedEvent.getSource();
|
||||
openSergoTrafficExchanger.subscribeTrafficRouterConfig(
|
||||
openSergoConfigProperties.getNamespace(), targetService);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo.util;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcher;
|
||||
import com.alibaba.cloud.commons.matcher.StringMatcherType;
|
||||
import com.google.protobuf.Internal;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import io.envoyproxy.envoy.config.route.v3.ClusterSpecifierPlugin;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.RegexMatcher;
|
||||
import io.opensergo.proto.router.v1.ClusterFallbackConfig_ClusterConfig;
|
||||
|
||||
/**
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
public final class ConvUtils {
|
||||
|
||||
private ConvUtils() {
|
||||
|
||||
}
|
||||
|
||||
public static String getOpenSergoHost(String endpoint) {
|
||||
if (StringUtils.isNotEmpty(endpoint)) {
|
||||
return endpoint.split(":")[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer getOpenSergoPort(String endpoint) {
|
||||
if (StringUtils.isNotEmpty(endpoint)) {
|
||||
String portStr = endpoint.split(":")[1];
|
||||
return Integer.valueOf(portStr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ClusterFallbackConfig_ClusterConfig convFallbackClusterConfig(
|
||||
ClusterSpecifierPlugin clusterSpecifierPlugin)
|
||||
throws InvalidProtocolBufferException {
|
||||
Message defaultInstance = Internal
|
||||
.getDefaultInstance(ClusterFallbackConfig_ClusterConfig.class);
|
||||
return (ClusterFallbackConfig_ClusterConfig) defaultInstance.getParserForType()
|
||||
.parseFrom(clusterSpecifierPlugin.getExtension().getTypedConfig()
|
||||
.getValue());
|
||||
}
|
||||
|
||||
public static StringMatcher convStringMatcher(
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher stringMatcher) {
|
||||
if (stringMatcher == null) {
|
||||
return null;
|
||||
}
|
||||
boolean isIgnoreCase = stringMatcher.getIgnoreCase();
|
||||
String exact = stringMatcher.getExact();
|
||||
String prefix = stringMatcher.getPrefix();
|
||||
String suffix = stringMatcher.getSuffix();
|
||||
String contains = stringMatcher.getContains();
|
||||
String regex = stringMatcher.getSafeRegex().getRegex();
|
||||
if (StringUtils.isNotBlank(exact)) {
|
||||
return new StringMatcher(exact, StringMatcherType.EXACT, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(prefix)) {
|
||||
return new StringMatcher(prefix, StringMatcherType.PREFIX, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(suffix)) {
|
||||
return new StringMatcher(suffix, StringMatcherType.SUFFIX, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(contains)) {
|
||||
return new StringMatcher(contains, StringMatcherType.CONTAIN, isIgnoreCase);
|
||||
}
|
||||
if (StringUtils.isNotBlank(regex)) {
|
||||
return new StringMatcher(regex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static io.envoyproxy.envoy.type.matcher.v3.StringMatcher headerMatch2StringMatch(
|
||||
io.envoyproxy.envoy.config.route.v3.HeaderMatcher headerMatcher) {
|
||||
if (headerMatcher == null) {
|
||||
return null;
|
||||
}
|
||||
if (headerMatcher.getPresentMatch()) {
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
|
||||
.newBuilder();
|
||||
return builder.setSafeRegex(RegexMatcher.newBuilder().build())
|
||||
.setIgnoreCase(true).build();
|
||||
}
|
||||
if (!headerMatcher.hasStringMatch()) {
|
||||
io.envoyproxy.envoy.type.matcher.v3.StringMatcher.Builder builder = io.envoyproxy.envoy.type.matcher.v3.StringMatcher
|
||||
.newBuilder();
|
||||
String exactMatch = headerMatcher.getExactMatch();
|
||||
String containsMatch = headerMatcher.getContainsMatch();
|
||||
String prefixMatch = headerMatcher.getPrefixMatch();
|
||||
String suffixMatch = headerMatcher.getSuffixMatch();
|
||||
RegexMatcher safeRegex = headerMatcher.getSafeRegexMatch();
|
||||
if (!StringUtils.isEmpty(exactMatch)) {
|
||||
builder.setExact(exactMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(containsMatch)) {
|
||||
builder.setContains(containsMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(prefixMatch)) {
|
||||
builder.setPrefix(prefixMatch);
|
||||
}
|
||||
else if (!StringUtils.isEmpty(suffixMatch)) {
|
||||
builder.setSuffix(suffixMatch);
|
||||
}
|
||||
else if (safeRegex.isInitialized()) {
|
||||
builder.setSafeRegex(safeRegex);
|
||||
}
|
||||
return builder.setIgnoreCase(true).build();
|
||||
}
|
||||
return headerMatcher.getStringMatch();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.alibaba.cloud.governance.opensergo.OpenSergoAutoConfig
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.opensergo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.labelrouting.UnifiedRouteDataStructure;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.envoyproxy.envoy.config.route.v3.HeaderMatcher;
|
||||
import io.envoyproxy.envoy.config.route.v3.Route;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteAction;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteConfiguration;
|
||||
import io.envoyproxy.envoy.config.route.v3.RouteMatch;
|
||||
import io.envoyproxy.envoy.config.route.v3.VirtualHost;
|
||||
import io.envoyproxy.envoy.type.matcher.v3.StringMatcher;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author panxiaojun233
|
||||
* @author <a href="m13201628570@163.com"></a>
|
||||
*/
|
||||
public class OpenSergoRuleTests {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenSergoRuleTests.class);
|
||||
|
||||
private OpenSergoTrafficRouterParser openSergoTrafficRouterParser = new OpenSergoTrafficRouterParser();
|
||||
|
||||
@Test
|
||||
public void testOpenSergoTrafficRouterTransform() throws Exception {
|
||||
HeaderMatcher headerMatcher = HeaderMatcher.newBuilder().setName("x-tag")
|
||||
.setStringMatch(StringMatcher.newBuilder().setExact("v2").buildPartial())
|
||||
.build();
|
||||
RouteMatch routeMatch = RouteMatch.newBuilder().addHeaders(headerMatcher).build();
|
||||
Route route = Route.newBuilder().setMatch(routeMatch)
|
||||
.setRoute(RouteAction.newBuilder()
|
||||
.setCluster(
|
||||
"outbound||v2|service-provider.default.svc.cluster.local")
|
||||
.build())
|
||||
.build();
|
||||
VirtualHost virtualHost = VirtualHost.newBuilder().setName("service-provider")
|
||||
.addDomains("service-provider.default.svc.cluster.local").addRoutes(route)
|
||||
.build();
|
||||
RouteConfiguration routeConfiguration = RouteConfiguration.newBuilder()
|
||||
.setName("service-provider").addVirtualHosts(virtualHost).build();
|
||||
List<RouteConfiguration> routeConfigurations = new ArrayList<>();
|
||||
routeConfigurations.add(routeConfiguration);
|
||||
Collection<UnifiedRouteDataStructure> rules = openSergoTrafficRouterParser
|
||||
.resolveLabelRouting(routeConfigurations);
|
||||
log.info("TrafficRouter rules are {}", JSONObject.toJSONString(rules));
|
||||
Assert.assertEquals(rules.size(), 1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-starters</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-auth</artifactId>
|
||||
<name>Spring Cloud Starter Alibaba Governance Authentication</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.b_c</groupId>
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>${jose4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.16</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth;
|
||||
|
||||
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
|
||||
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(AuthenticationAutoConfiguration.class)
|
||||
public class AuthValidatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public AuthValidator authValidator(AuthRepository authRepository) {
|
||||
return new AuthValidator(authRepository);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth;
|
||||
|
||||
import com.alibaba.cloud.governance.auth.listener.AuthListener;
|
||||
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
// We need to auto config the class before spring cloud alibaba istio module, to prevent
|
||||
// event publisher hang permanently.
|
||||
@AutoConfigureOrder(AuthenticationAutoConfiguration.AUTH_AUTO_CONFIG_ORDER)
|
||||
|
||||
public class AuthenticationAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Order of auth auto config.
|
||||
*/
|
||||
public static final int AUTH_AUTO_CONFIG_ORDER = 9;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AuthRepository authRepository() {
|
||||
return new AuthRepository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthListener authListener(AuthRepository authRepository) {
|
||||
return new AuthListener(authRepository);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth;
|
||||
|
||||
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
|
||||
import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(AuthenticationAutoConfiguration.class)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
|
||||
matchIfMissing = true)
|
||||
public class XdsWebAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
|
||||
matchIfMissing = true)
|
||||
public AuthWebInterceptor authWebInterceptor(AuthValidator authValidator) {
|
||||
return new AuthWebInterceptor(authValidator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
|
||||
matchIfMissing = true)
|
||||
public XdsWebMvcConfigurer xdsWebMvcConfigurer() {
|
||||
return new XdsWebMvcConfigurer();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth;
|
||||
|
||||
import com.alibaba.cloud.governance.auth.validator.AuthValidator;
|
||||
import com.alibaba.cloud.governance.auth.webflux.AuthWebFluxFilter;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureAfter(AuthValidatorAutoConfiguration.class)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
|
||||
matchIfMissing = true)
|
||||
public class XdsWebFluxAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.governance.auth.enabled",
|
||||
matchIfMissing = true)
|
||||
public AuthWebFluxFilter authWebFluxFilter(AuthValidator authValidator) {
|
||||
return new AuthWebFluxFilter(authValidator);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.alibaba.cloud.governance.auth.webmvc.AuthWebInterceptor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public class XdsWebMvcConfigurer implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private Optional<AuthWebInterceptor> authWebInterceptor;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
if (!authWebInterceptor.isPresent()) {
|
||||
return;
|
||||
}
|
||||
registry.addInterceptor(authWebInterceptor.get());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth.listener;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.AuthRules;
|
||||
import com.alibaba.cloud.commons.governance.event.AuthDataChangedEvent;
|
||||
import com.alibaba.cloud.governance.auth.repository.AuthRepository;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> To receive the auth data when it is
|
||||
* changed by user.
|
||||
*/
|
||||
public class AuthListener implements ApplicationListener<AuthDataChangedEvent> {
|
||||
|
||||
private final AuthRepository authRepository;
|
||||
|
||||
public AuthListener(AuthRepository authRepository) {
|
||||
this.authRepository = authRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AuthDataChangedEvent event) {
|
||||
AuthRules authRules = event.getAuthRules();
|
||||
if (authRules.getAllowAuthRules() != null) {
|
||||
authRepository.setAllowAuthRule(authRules.getAllowAuthRules());
|
||||
}
|
||||
if (authRules.getDenyAuthRules() != null) {
|
||||
authRepository.setDenyAuthRules(authRules.getDenyAuthRules());
|
||||
}
|
||||
if (authRules.getJwtRules() != null) {
|
||||
authRepository.setJwtRule(authRules.getJwtRules());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth.repository;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.AuthRule;
|
||||
import com.alibaba.cloud.commons.governance.auth.rule.JwtRule;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a> To store auth rules in Spring Cloud
|
||||
* Alibaba.
|
||||
*/
|
||||
public class AuthRepository {
|
||||
|
||||
private Map<String, AuthRule> allowAuthRules = new HashMap<>();
|
||||
|
||||
private Map<String, AuthRule> denyAuthRules = new HashMap<>();
|
||||
|
||||
private Map<String, JwtRule> jwtRules = new HashMap<>();
|
||||
|
||||
public AuthRepository() {
|
||||
|
||||
}
|
||||
|
||||
public AuthRepository(Map<String, AuthRule> allowAuthRules,
|
||||
Map<String, AuthRule> denyAuthRules, Map<String, JwtRule> jwtRules) {
|
||||
this.allowAuthRules = allowAuthRules;
|
||||
this.denyAuthRules = denyAuthRules;
|
||||
this.jwtRules = jwtRules;
|
||||
}
|
||||
|
||||
public Map<String, AuthRule> getAllowAuthRules() {
|
||||
return allowAuthRules;
|
||||
}
|
||||
|
||||
public Map<String, AuthRule> getDenyAuthRules() {
|
||||
return denyAuthRules;
|
||||
}
|
||||
|
||||
public Map<String, JwtRule> getJwtRules() {
|
||||
return jwtRules;
|
||||
}
|
||||
|
||||
public void setAllowAuthRule(Map<String, AuthRule> allowAuthRules) {
|
||||
this.allowAuthRules = allowAuthRules;
|
||||
}
|
||||
|
||||
public void setDenyAuthRules(Map<String, AuthRule> denyAuthRules) {
|
||||
this.denyAuthRules = denyAuthRules;
|
||||
}
|
||||
|
||||
public void setJwtRule(Map<String, JwtRule> jwtRules) {
|
||||
this.jwtRules = jwtRules;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.cloud.governance.auth.util;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
||||
/**
|
||||
* @author musi
|
||||
* @author <a href="liuziming@buaa.edu.cn"></a>
|
||||
*/
|
||||
public final class IpUtil {
|
||||
|
||||
private static final String UNKNOWN = "unknown";
|
||||
|
||||
private IpUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static String getRemoteIpAddress(ServerHttpRequest request) {
|
||||
String ip = request.getHeaders().getFirst("X-Forwarded-For");
|
||||
if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
if (ip.contains(",")) {
|
||||
ip = ip.split(",")[0];
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
if (request.getRemoteAddress() != null
|
||||
&& request.getRemoteAddress().getAddress() != null) {
|
||||
ip = request.getRemoteAddress().getAddress().getHostAddress();
|
||||
}
|
||||
}
|
||||
return StringUtils.isEmpty(ip) ? null : ip;
|
||||
}
|
||||
|
||||
public static String getRemoteIpAddress(ServletRequest request) {
|
||||
if (!(request instanceof HttpServletRequest)) {
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
String ip = httpServletRequest.getHeader("X-Forwarded-For");
|
||||
if (StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
if (ip.contains(",")) {
|
||||
ip = ip.split(",")[0];
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
|
||||
if (httpServletRequest.getRemoteAddr() != null) {
|
||||
ip = httpServletRequest.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
return StringUtils.isEmpty(ip) ? null : ip;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue