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; + } +}