From d0bb9f8560b0e3ac1333e0bcf41c279c4c3dcaf3 Mon Sep 17 00:00:00 2001
From: daizhenyu <1449308021@qq.com>
Date: Wed, 9 Oct 2024 10:23:41 +0800
Subject: [PATCH] xDS router and lb integration test: demo and test case
Signed-off-by: daizhenyu <1449308021@qq.com>
---
.../xds-service-test/config/plugins.yaml | 1 +
.../xds-service-test/pom.xml | 14 ++
.../product/spring-cloud-client/Dockerfile | 7 +
.../product/spring-cloud-client/start.sh | 1 +
.../spring-client-sermant-xds-router.yaml | 47 ++++
...pring-cloud-client-sermant-xds-router.yaml | 47 ++++
.../router/deployment/spring-server.yaml | 77 +++++++
.../spring-server-destination-random.yaml | 16 ++
.../spring-server-destination-robin.yaml | 16 ++
.../spring-server-virtureservice.yaml | 56 +++++
.../xds-service-test/spring-client/pom.xml | 6 +
.../spring/client/SpringRouterController.java | 78 +++++++
.../spring-cloud-client/pom.xml | 100 ++++++++
.../client/SpringCloudClientApplication.java | 38 +++
.../client/SpringRouterController.java | 216 ++++++++++++++++++
.../client/config/RestTemplateConfig.java | 42 ++++
.../src/main/resources/application.yml | 9 +
.../xds-service-test/spring-server/pom.xml | 4 +
.../spring/server/RouterServerController.java | 43 ++++
.../src/main/resources/application.properties | 1 -
.../src/main/resources/application.yml | 9 +
.../xds-service-discovery/pom.xml | 19 ++
.../xds/service/router/XdsRouterTest.java | 151 ++++++++++++
23 files changed, 997 insertions(+), 1 deletion(-)
create mode 100644 sermant-integration-tests/xds-service-test/product/spring-cloud-client/Dockerfile
create mode 100644 sermant-integration-tests/xds-service-test/product/spring-cloud-client/start.sh
create mode 100644 sermant-integration-tests/xds-service-test/script/router/deployment/spring-client-sermant-xds-router.yaml
create mode 100644 sermant-integration-tests/xds-service-test/script/router/deployment/spring-cloud-client-sermant-xds-router.yaml
create mode 100644 sermant-integration-tests/xds-service-test/script/router/deployment/spring-server.yaml
create mode 100644 sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-random.yaml
create mode 100644 sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-robin.yaml
create mode 100644 sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-virtureservice.yaml
create mode 100644 sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringRouterController.java
create mode 100644 sermant-integration-tests/xds-service-test/spring-cloud-client/pom.xml
create mode 100644 sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringCloudClientApplication.java
create mode 100644 sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringRouterController.java
create mode 100644 sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/config/RestTemplateConfig.java
create mode 100644 sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/resources/application.yml
create mode 100644 sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/RouterServerController.java
delete mode 100644 sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.properties
create mode 100644 sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.yml
create mode 100644 sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/router/XdsRouterTest.java
diff --git a/sermant-integration-tests/xds-service-test/config/plugins.yaml b/sermant-integration-tests/xds-service-test/config/plugins.yaml
index 4b642dceed..817395e1ca 100644
--- a/sermant-integration-tests/xds-service-test/config/plugins.yaml
+++ b/sermant-integration-tests/xds-service-test/config/plugins.yaml
@@ -1,2 +1,3 @@
plugins:
- xds-service-discovery
+ - service-router
diff --git a/sermant-integration-tests/xds-service-test/pom.xml b/sermant-integration-tests/xds-service-test/pom.xml
index dc074d90cf..6aef01111c 100644
--- a/sermant-integration-tests/xds-service-test/pom.xml
+++ b/sermant-integration-tests/xds-service-test/pom.xml
@@ -16,11 +16,25 @@
8
8
2.7.17
+ 2021.0.3
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${springcloud.version}
+ pom
+ import
+
+
+
+
spring-client
spring-server
xds-service-discovery
+ spring-cloud-client
diff --git a/sermant-integration-tests/xds-service-test/product/spring-cloud-client/Dockerfile b/sermant-integration-tests/xds-service-test/product/spring-cloud-client/Dockerfile
new file mode 100644
index 0000000000..f7dea4a55e
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-cloud-client/Dockerfile
@@ -0,0 +1,7 @@
+FROM openjdk:8
+WORKDIR /home
+COPY agent/ /home/agent
+COPY spring-cloud-client.jar /home/spring-cloud-client.jar
+COPY start.sh /home
+RUN chmod -R 777 /home
+ENTRYPOINT ["sh", "/home/start.sh"]
diff --git a/sermant-integration-tests/xds-service-test/product/spring-cloud-client/start.sh b/sermant-integration-tests/xds-service-test/product/spring-cloud-client/start.sh
new file mode 100644
index 0000000000..997b94bd6f
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-cloud-client/start.sh
@@ -0,0 +1 @@
+exec java -jar /home/spring-cloud-client.jar
diff --git a/sermant-integration-tests/xds-service-test/script/router/deployment/spring-client-sermant-xds-router.yaml b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-client-sermant-xds-router.yaml
new file mode 100644
index 0000000000..682f88fb3c
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-client-sermant-xds-router.yaml
@@ -0,0 +1,47 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-client
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-client
+ template:
+ metadata:
+ labels:
+ app: spring-client
+ spec:
+ containers:
+ - name: spring-client
+ image: spring-client:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
+ env:
+ - name: agent_service_dynamic_config_enable
+ value: "false"
+ - name: agent_service_xds_service_enable
+ value: "true"
+ - name: router_plugin_enabled_xds_route
+ value: "true"
+ - name: xds_service_discovery_enabled
+ value: "false"
+ - name: JAVA_TOOL_OPTIONS
+ value: "-javaagent:/home/agent/sermant-agent.jar"
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-client
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8080
+ targetPort: 8080
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-client
diff --git a/sermant-integration-tests/xds-service-test/script/router/deployment/spring-cloud-client-sermant-xds-router.yaml b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-cloud-client-sermant-xds-router.yaml
new file mode 100644
index 0000000000..b5de30f028
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-cloud-client-sermant-xds-router.yaml
@@ -0,0 +1,47 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-cloud-client
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-cloud-client
+ template:
+ metadata:
+ labels:
+ app: spring-cloud-client
+ spec:
+ containers:
+ - name: spring-cloud-client
+ image: spring-cloud-client:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8082
+ env:
+ - name: agent_service_dynamic_config_enable
+ value: "false"
+ - name: agent_service_xds_service_enable
+ value: "true"
+ - name: router_plugin_enabled-xds-route
+ value: "true"
+ - name: ZOOKEEPER_IP
+ value: "zookeeper.default.svc.cluster.local"
+ - name: JAVA_TOOL_OPTIONS
+ value: "-javaagent:/home/agent/sermant-agent.jar"
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-cloud-client
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8082
+ targetPort: 8082
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-cloud-client
diff --git a/sermant-integration-tests/xds-service-test/script/router/deployment/spring-server.yaml b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-server.yaml
new file mode 100644
index 0000000000..3daac6ace0
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/deployment/spring-server.yaml
@@ -0,0 +1,77 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-server-v1
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-server
+ version: v1
+ template:
+ metadata:
+ labels:
+ app: spring-server
+ version: v1
+ spec:
+ containers:
+ - name: spring-server
+ image: spring-server:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8081
+ env:
+ - name: SERVER_VERSION
+ value: "v1"
+ - name: ZOOKEEPER_IP
+ value: "zookeeper.default.svc.cluster.local"
+ - name: ZOOKEEPER_ENABLED
+ value: "true"
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-server-v2
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-server
+ version: v2
+ template:
+ metadata:
+ labels:
+ app: spring-server
+ version: v2
+ spec:
+ containers:
+ - name: spring-server
+ image: spring-server:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8081
+ env:
+ - name: SERVER_VERSION
+ value: "v2"
+ - name: ZOOKEEPER_IP
+ value: "zookeeper.default.svc.cluster.local"
+ - name: ZOOKEEPER_ENABLED
+ value: "true"
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-server
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8081
+ targetPort: 8081
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-server
diff --git a/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-random.yaml b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-random.yaml
new file mode 100644
index 0000000000..45c1a008d1
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-random.yaml
@@ -0,0 +1,16 @@
+apiVersion: networking.istio.io/v1alpha3
+kind: DestinationRule
+metadata:
+ name: spring-server-destinationrule-random
+spec:
+ host: spring-server.default.svc.cluster.local
+ trafficPolicy:
+ loadBalancer:
+ simple: RANDOM
+ subsets:
+ - name: v1
+ labels:
+ version: v1
+ - name: v2
+ labels:
+ version: v2
diff --git a/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-robin.yaml b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-robin.yaml
new file mode 100644
index 0000000000..cca1245544
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-destination-robin.yaml
@@ -0,0 +1,16 @@
+apiVersion: networking.istio.io/v1alpha3
+kind: DestinationRule
+metadata:
+ name: spring-server-destinationrule-robin
+spec:
+ host: spring-server.default.svc.cluster.local
+ trafficPolicy:
+ loadBalancer:
+ simple: ROUND_ROBIN
+ subsets:
+ - name: v1
+ labels:
+ version: v1
+ - name: v2
+ labels:
+ version: v2
diff --git a/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-virtureservice.yaml b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-virtureservice.yaml
new file mode 100644
index 0000000000..bddefb5d10
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/router/router-rule/spring-server-virtureservice.yaml
@@ -0,0 +1,56 @@
+apiVersion: networking.istio.io/v1alpha3
+kind: VirtualService
+metadata:
+ name: spring-server-virtualservice
+spec:
+ hosts:
+ - spring-server
+ http:
+ - name: "v1-routes"
+ match:
+ - headers:
+ version:
+ exact: v1
+ uri:
+ exact: /router
+ ignoreUriCase: false
+ route:
+ - destination:
+ host: spring-server
+ subset: v1
+ port:
+ number: 8081
+ - name: "v2-routes"
+ match:
+ - headers:
+ version:
+ exact: v2
+ uri:
+ prefix: /
+ ignoreUriCase: false
+ route:
+ - destination:
+ host: spring-server
+ port:
+ number: 8081
+ - name: "base-route"
+ match:
+ - headers:
+ version:
+ exact: base
+ uri:
+ exact: /router
+ ignoreUriCase: false
+ route:
+ - destination:
+ host: spring-server
+ subset: v1
+ port:
+ number: 8081
+ weight: 0
+ - destination:
+ host: spring-server
+ subset: v2
+ port:
+ number: 8081
+ weight: 100
diff --git a/sermant-integration-tests/xds-service-test/spring-client/pom.xml b/sermant-integration-tests/xds-service-test/spring-client/pom.xml
index b2cfbabcb2..a44b777a3c 100644
--- a/sermant-integration-tests/xds-service-test/spring-client/pom.xml
+++ b/sermant-integration-tests/xds-service-test/spring-client/pom.xml
@@ -15,6 +15,7 @@
8
8
4.5.13
+ 2.7.5
@@ -28,6 +29,11 @@
httpclient
${httpclient.version}
+
+ com.squareup.okhttp
+ okhttp
+ ${okhttp2.version}
+
diff --git a/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringRouterController.java b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringRouterController.java
new file mode 100644
index 0000000000..af50348dd0
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringRouterController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.demo.spring.client;
+
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+/**
+ * SpringRouterController
+ *
+ * @author daizhenyu
+ * @since 2024-10-08
+ **/
+@RequestMapping("router")
+@RestController
+public class SpringRouterController {
+ private static final String VERSION = "version";
+
+ private static final int SUCCEED_CODE = 200;
+
+ private static final String ROUTER_METHOD_PATH = "/router";
+
+ /**
+ * test okhttp2 routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("okHttp2")
+ public String testOkHttp2Routing(String host, String version) {
+ String url = buildUrl(host);
+ OkHttpClient client = new OkHttpClient();
+ Request request = new Request.Builder()
+ .url(url)
+ .addHeader(VERSION, version)
+ .build();
+ try {
+ Response response = client.newCall(request).execute();
+ int statusCode = response.code();
+ if (statusCode == SUCCEED_CODE) {
+ return response.body().string();
+ } else {
+ return "";
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ private String buildUrl(String host) {
+ StringBuilder urlBuilder = new StringBuilder();
+ urlBuilder.append("http://");
+ urlBuilder.append(host);
+ urlBuilder.append(ROUTER_METHOD_PATH);
+ return urlBuilder.toString();
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-cloud-client/pom.xml b/sermant-integration-tests/xds-service-test/spring-cloud-client/pom.xml
new file mode 100644
index 0000000000..ca1b2b0687
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-cloud-client/pom.xml
@@ -0,0 +1,100 @@
+
+
+
+ xds-service-test
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ spring-cloud-client
+
+
+ 8
+ 8
+ 4.5.13
+ 4.1.4
+ 4.9.3
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring.version}
+
+
+ org.springframework.cloud
+ spring-cloud-starter-zookeeper-discovery
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+
+
+ com.squareup.okhttp3
+ okhttp
+ ${okhttp3.version}
+
+
+ org.apache.httpcomponents
+ httpasyncclient
+ ${httpclient.async.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.version}
+
+ io.sermant.demo.springcloud.client.SpringCloudClientApplication
+ spring-cloud-client
+ ${pom.basedir}/../product/spring-cloud-client
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 2.5
+ false
+
+
+ agent-clean
+ clean
+
+ clean
+
+
+
+
+ ${pom.basedir}/../product/spring-cloud-client
+
+ spring-cloud-client.jar
+
+
+
+
+
+
+
+
+
+
diff --git a/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringCloudClientApplication.java b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringCloudClientApplication.java
new file mode 100644
index 0000000000..83e0802605
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringCloudClientApplication.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.demo.springcloud.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * SpringCloudClientApplication
+ *
+ * @author daizhenyu
+ * @since 2024-09-23
+ **/
+@SpringBootApplication
+public class SpringCloudClientApplication {
+ /**
+ * main
+ *
+ * @param args args
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(SpringCloudClientApplication.class, args);
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringRouterController.java b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringRouterController.java
new file mode 100644
index 0000000000..1df321d0e0
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/SpringRouterController.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.demo.springcloud.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * SpringRouterController
+ *
+ * @author daizhenyu
+ * @since 2024-09-23
+ **/
+@RequestMapping("router")
+@RestController
+public class SpringRouterController {
+ private static final String VERSION = "version";
+
+ private static final int SUCCEED_CODE = 200;
+
+ private static final String ROUTER_METHOD_PATH = "/router";
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ /**
+ * test httpclient routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("httpClient")
+ public String testHttpClientRouting(String host, String version) {
+ String url = buildUrl(host);
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ HttpGet request = new HttpGet(url);
+ request.addHeader(VERSION, version);
+ HttpResponse response = httpClient.execute(request);
+ int statusCode = response.getStatusLine().getStatusCode();
+
+ if (statusCode == HttpStatus.SC_OK) {
+ return EntityUtils.toString(response.getEntity());
+ } else {
+ return "";
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ /**
+ * test jdk http routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("jdkHttp")
+ public String testJdkHttpRouting(String host, String version) {
+ String url = buildUrl(host);
+ HttpURLConnection connection = null;
+ BufferedReader reader = null;
+ try {
+ URL requestUrl = new URL(url);
+ connection = (HttpURLConnection) requestUrl.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty(VERSION, version);
+ int responseCode = connection.getResponseCode();
+ if (responseCode == HttpURLConnection.HTTP_OK) {
+ reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ return response.toString();
+ } else {
+ return "";
+ }
+ } catch (IOException e) {
+ return "";
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ /**
+ * test http async client routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("httpAsyncClient")
+ public String testHttpAsyncClientRouting(String host, String version) {
+ String url = buildUrl(host);
+ try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
+ httpclient.start();
+ HttpGet request = new HttpGet(url);
+ request.setHeader(VERSION, version);
+ Future future = httpclient.execute(request, null);
+ HttpResponse response = future.get();
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode == HttpStatus.SC_OK) {
+ return EntityUtils.toString(response.getEntity());
+ } else {
+ return "";
+ }
+ } catch (IOException | InterruptedException | ExecutionException e) {
+ return "";
+ }
+ }
+
+ /**
+ * test okhttp3 routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("okHttp3")
+ public String testOkHttp3Routing(String host, String version) {
+ String url = buildUrl(host);
+ okhttp3.OkHttpClient client = new okhttp3.OkHttpClient();
+ okhttp3.Request request = new okhttp3.Request.Builder()
+ .url(url)
+ .addHeader(VERSION, version)
+ .build();
+ try (okhttp3.Response response = client.newCall(request).execute()) {
+ int statusCode = response.code();
+ if (statusCode == SUCCEED_CODE) {
+ return response.body().string();
+ } else {
+ return "";
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ /**
+ * test RestTemplate routing
+ *
+ * @param host host
+ * @param version version
+ * @return result
+ */
+ @RequestMapping("restTemplate")
+ public String testRestTemplateRouting(String host, String version) {
+ String url = buildUrl(host);
+ HttpHeaders headers = new HttpHeaders();
+ headers.add(VERSION, version);
+ HttpEntity entity = new HttpEntity<>(headers);
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ entity,
+ String.class
+ );
+ return response.getBody();
+ }
+
+ private String buildUrl(String host) {
+ StringBuilder urlBuilder = new StringBuilder();
+ urlBuilder.append("http://");
+ urlBuilder.append(host);
+ urlBuilder.append(ROUTER_METHOD_PATH);
+ return urlBuilder.toString();
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/config/RestTemplateConfig.java b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/config/RestTemplateConfig.java
new file mode 100644
index 0000000000..110ca3bb5c
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/java/io/sermant/demo/springcloud/client/config/RestTemplateConfig.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.demo.springcloud.client.config;
+
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * RestTemplateConfig
+ *
+ * @author daizhenyu
+ * @since 2024-09-23
+ **/
+@Configuration
+public class RestTemplateConfig {
+ /**
+ * RestTemplate bean
+ *
+ * @return RestTemplate bean
+ */
+ @Bean
+ @LoadBalanced
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/resources/application.yml b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/resources/application.yml
new file mode 100644
index 0000000000..0ca8c3a10f
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-cloud-client/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ application:
+ name: spring-cloud-client
+ cloud:
+ zookeeper:
+ connect-string: ${ZOOKEEPER_IP}:2181
+ enabled: true
+server:
+ port: 8082
diff --git a/sermant-integration-tests/xds-service-test/spring-server/pom.xml b/sermant-integration-tests/xds-service-test/spring-server/pom.xml
index cf33231623..62e66c05a7 100644
--- a/sermant-integration-tests/xds-service-test/spring-server/pom.xml
+++ b/sermant-integration-tests/xds-service-test/spring-server/pom.xml
@@ -22,6 +22,10 @@
spring-boot-starter-web
${spring.version}
+
+ org.springframework.cloud
+ spring-cloud-starter-zookeeper-discovery
+
diff --git a/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/RouterServerController.java b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/RouterServerController.java
new file mode 100644
index 0000000000..1e8d6ec270
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/RouterServerController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.demo.spring.server;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * RouterServerController
+ *
+ * @author daizhenyu
+ * @since 2024-09-23
+ **/
+@RestController
+public class RouterServerController {
+ @Value("${SERVER_VERSION:1.0.0}")
+ private String version;
+
+ /**
+ * router
+ *
+ * @return result
+ */
+ @RequestMapping("router")
+ public String router() {
+ return version;
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.properties b/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.properties
deleted file mode 100644
index 4d360de145..0000000000
--- a/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-server.port=8081
diff --git a/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.yml b/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.yml
new file mode 100644
index 0000000000..2d4efb462b
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-server/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+spring:
+ application:
+ name: spring-server
+ cloud:
+ zookeeper:
+ connect-string: ${ZOOKEEPER_IP}:2181
+ enabled: ${ZOOKEEPER_ENABLED:false}
+server:
+ port: 8081
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/pom.xml b/sermant-integration-tests/xds-service-test/xds-service-discovery/pom.xml
index 36664deb01..6c59a1fd66 100644
--- a/sermant-integration-tests/xds-service-test/xds-service-discovery/pom.xml
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/pom.xml
@@ -146,6 +146,25 @@
+
+ copy-router-plugin
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/pluginPackage
+ true
+
+
+ ${sermant.source.dir}/pluginPackage
+
+ service-router/**
+
+
+
+
+
copy-agent
package
diff --git a/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/router/XdsRouterTest.java b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/router/XdsRouterTest.java
new file mode 100644
index 0000000000..980abfae26
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/router/XdsRouterTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024-2024 Sermant Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.sermant.xds.service.router;
+
+import io.sermant.xds.service.utils.HttpRequestUtils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+/**
+ * xDS router test
+ *
+ * @author daizhenyu
+ * @since 2024-10-08
+ **/
+public class XdsRouterTest {
+ /**
+ * test xds router with header and path
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "ROUTER_HEADER")
+ public void testRouterWithHeaderAndPath() {
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8080/router/okHttp2?host=spring-server&version=v1"));
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpClient?host=spring-server&version=v1"));
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/jdkHttp?host=spring-server&version=v1"));
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpAsyncClient?host=spring-server&version=v1"));
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/okHttp3?host=spring-server&version=v1"));
+ Assertions.assertEquals("v1",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/restTemplate?host=spring-server&version=v1"));
+ }
+
+ /**
+ * test xds router with random lb
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "ROUTER_RANDOM")
+ public void testRouterWithRandom() {
+ int[][] results = new int[5][2];
+
+ int count = 100;
+ while (count > 0) {
+ count--;
+ countCalls(results, 0,
+ HttpRequestUtils.doGet("http://127.0.0.1:8080/router/okHttp2?host=spring-server&version=v2"));
+ countCalls(results, 1,
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpClient?host=spring-server&version=v2"));
+ countCalls(results, 2,
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/jdkHttp?host=spring-server&version=v2"));
+ countCalls(results, 3,
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpAsyncClient?host=spring-server&version=v2"));
+ countCalls(results, 4,
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/okHttp3?host=spring-server&version=v2"));
+ }
+ Assertions.assertTrue(isRandom(results, 0), "okHttp2 random lb policy does not take effect");
+ Assertions.assertTrue(isRandom(results, 1), "httpClient random lb policy does not take effect");
+ Assertions.assertTrue(isRandom(results, 2), "jdkHttp random lb policy does not take effect");
+ Assertions.assertTrue(isRandom(results, 3), "httpAsyncClient random lb policy does not take effect");
+ Assertions.assertTrue(isRandom(results, 4), "okHttp3 random lb policy does not take effect");
+ }
+
+ /**
+ * test xds router with round-robin lb
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "ROUND_ROBIN")
+ public void testRouterWithRoundRobin() {
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8080/router/okHttp2?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8080/router/okHttp2?host=spring-server&version=v2"));
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpClient?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpClient?host=spring-server&version=v2"));
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/jdkHttp?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/jdkHttp?host=spring-server&version=v2"));
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpAsyncClient?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpAsyncClient?host=spring-server&version=v2"));
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/okHttp3?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/okHttp3?host=spring-server&version=v2"));
+ Assertions.assertNotEquals(
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/restTemplate?host=spring-server&version=v2"),
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/restTemplate?host=spring-server&version=v2"));
+ }
+
+ /**
+ * test xds router with weighted cluster
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "ROUTER_WEIGHT")
+ public void testRouterWithWeight() {
+ int count = 3;
+ while (count > 0) {
+ count--;
+ Assertions.assertEquals("v2",
+ HttpRequestUtils.doGet("http://127.0.0.1:8080/router/okHttp2?host=spring-server&version=base"));
+ Assertions.assertEquals("v2",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/httpClient?host=spring-server&version=base"));
+ Assertions.assertEquals("v2",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/jdkHttp?host=spring-server&version=base"));
+ Assertions.assertEquals("v2",
+ HttpRequestUtils
+ .doGet("http://127.0.0.1:8082/router/httpAsyncClient?host=spring-server&version=base"));
+ Assertions.assertEquals("v2",
+ HttpRequestUtils.doGet("http://127.0.0.1:8082/router/okHttp3?host=spring-server&version=base"));
+ Assertions.assertEquals("v2",
+ HttpRequestUtils
+ .doGet("http://127.0.0.1:8082/router/restTemplate?host=spring-server&version=base"));
+ }
+ }
+
+ private void countCalls(int[][] results, int index, String result) {
+ switch (result) {
+ case "v1":
+ results[index][0]++;
+ break;
+ case "v2":
+ results[index][1]++;
+ break;
+ }
+ }
+
+ private boolean isRandom(int[][] results, int index) {
+ int differenceValue = Math.abs(results[index][0] - results[index][1]);
+ if (differenceValue <= 20) {
+ return true;
+ }
+ return false;
+ }
+}