diff --git a/.github/actions/common/xds-service/action.yml b/.github/actions/common/xds-service/action.yml
new file mode 100644
index 0000000000..cd4084f974
--- /dev/null
+++ b/.github/actions/common/xds-service/action.yml
@@ -0,0 +1,51 @@
+name: "xDS Service Common Test"
+description: "Auto common test for xds service"
+runs:
+ using: composite
+ steps:
+ - name: start minikube
+ uses: medyagh/setup-minikube@latest
+ id: minikube
+ with:
+ minikube-version: 1.31.2
+ driver: docker
+ container-runtime: docker
+ - name: Delay for 10 seconds to ready k8s
+ shell: bash
+ run: sleep 10s
+ - name: check k8s
+ shell: bash
+ run: |
+ kubectl cluster-info
+ kubectl get pods -n kube-system
+ echo "k8s version:" $(kubectl version)
+ - name: deploy istio
+ shell: bash
+ run: |
+ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.21.4 sh -
+ istio-1.21.4/bin/istioctl install -f sermant-integration-tests/xds-service-test/script/istio-operator.yaml --skip-confirmation
+ - name: Delay for 10 seconds to ready istio
+ shell: bash
+ run: |
+ sleep 10s
+ kubectl get pods -n istio-system
+ - name: Set up JDK ${{ env.javaVersion }}
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ env.javaVersion }}
+ distribution: 'adopt'
+ cache: maven
+ - name: get cached agent
+ uses: actions/cache@v3
+ with:
+ path: sermant-agent-*/
+ key: ${{ runner.os }}-agent-${{ github.run_id }}
+ - name: remove xds service product
+ shell: bash
+ run: |
+ rm -rf sermant-integration-tests/xds-service-test/product
+ - name: get cached xds service package
+ uses: actions/cache@v3
+ with:
+ path: sermant-integration-tests/xds-service-test/product/
+ key: ${{ runner.os }}-xds-service-product-${{ github.run_id }}
\ No newline at end of file
diff --git a/.github/actions/scenarios/xds-service/xds-service-discovery/client-envoy/action.yml b/.github/actions/scenarios/xds-service/xds-service-discovery/client-envoy/action.yml
new file mode 100644
index 0000000000..4b55487216
--- /dev/null
+++ b/.github/actions/scenarios/xds-service/xds-service-discovery/client-envoy/action.yml
@@ -0,0 +1,59 @@
+name: "xDS Service Discovery Test with Client Using Envoy"
+description: "Auto test for xds service discovery with Client Using Envoy"
+runs:
+ using: composite
+ steps:
+ - name: build docker image
+ shell: bash
+ run: |
+ cd sermant-integration-tests/xds-service-test/product/spring-server/
+ minikube image build -t spring-server:1.0.0 .
+ cd ../spring-client/
+ minikube image build -t spring-client:1.0.0 .
+ eval $(minikube docker-env)
+ docker images
+ - name: start spring-client using envoy
+ shell: bash
+ run: |
+ kubectl label namespace default istio-injection=enabled
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-close.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-client --timeout=10s
+ kubectl port-forward svc/spring-client 8080:8080 &
+ kubectl label namespace default istio-injection-
+ - name: Wait for spring-client pod to be ready
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ sleep 5s
+ - name: test one server
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ONE_SERVER_INSTANCE_ENVOY --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: stop spring-server
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
+ sleep 3s
+ - name: test the number of spring-server instances changes from 1 to 0
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ZERO_SERVER_INSTANCE_ENVOY --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: restart spring-server
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ sleep 10s
+ - name: test the number of spring-server instances changes from 0 to 1
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ONE_SERVER_INSTANCE_ENVOY --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: close all service
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-close.yaml
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
\ No newline at end of file
diff --git a/.github/actions/scenarios/xds-service/xds-service-discovery/sermant-only/action.yml b/.github/actions/scenarios/xds-service/xds-service-discovery/sermant-only/action.yml
new file mode 100644
index 0000000000..80907206bb
--- /dev/null
+++ b/.github/actions/scenarios/xds-service/xds-service-discovery/sermant-only/action.yml
@@ -0,0 +1,84 @@
+name: "xDS Service Discovery Test"
+description: "Auto test for xds service discovery"
+runs:
+ using: composite
+ steps:
+ - name: build docker image
+ shell: bash
+ run: |
+ cd sermant-integration-tests/xds-service-test/product/spring-server/
+ minikube image build -t spring-server:1.0.0 .
+ cd ../spring-client/
+ minikube image build -t spring-client:1.0.0 .
+ eval $(minikube docker-env)
+ docker images
+ - name: start spring-client and spring-server
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
+ - name: Wait for spring-client pod to be ready
+ shell: bash
+ run: |
+ kubectl wait --for=condition=ready pod -l app=spring-client --timeout=10s
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ kubectl port-forward svc/spring-client 8080:8080 &
+ - name: test one server
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ONE_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: stop spring-server
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
+ sleep 3s
+ - name: test the number of spring-server instances changes from 1 to 0
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ZERO_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: start spring-server with 2 replicas
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ sleep 2s
+ - name: test the number of spring-server instances changes from 0 to 2
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_TWO_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: create istio token
+ shell: bash
+ run: |
+ kubectl create serviceaccount istio-test
+ TOKEN=$(kubectl create token istio-test --duration=1h --audience=istio-ca)
+ kubectl create secret generic istio-test-secret \
+ --from-literal=token=${TOKEN} \
+ --type="istio.io/key-and-cert" \
+ -n default
+ - name: restart spring-client and sermant xds service use security mode
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
+ sleep 5s
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-subscribe-secure.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-client --timeout=10s
+ pkill -f "kubectl port-forward svc/spring-client"
+ kubectl port-forward svc/spring-client 8080:8080 &
+ - name: test sermant xds service use security mode
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=SECRET --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: test subscribe to get service instance
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_SUBSCRIBE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: close all service
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-subscribe-secure.yaml
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
\ No newline at end of file
diff --git a/.github/actions/scenarios/xds-service/xds-service-discovery/server-envoy/action.yml b/.github/actions/scenarios/xds-service/xds-service-discovery/server-envoy/action.yml
new file mode 100644
index 0000000000..8f20cd411b
--- /dev/null
+++ b/.github/actions/scenarios/xds-service/xds-service-discovery/server-envoy/action.yml
@@ -0,0 +1,58 @@
+name: "xDS Service Discovery Test with Server Using Envoy"
+description: "Auto test for xds service discovery with server using envoy"
+runs:
+ using: composite
+ steps:
+ - name: build docker image
+ shell: bash
+ run: |
+ cd sermant-integration-tests/xds-service-test/product/spring-server/
+ minikube image build -t spring-server:1.0.0 .
+ cd ../spring-client/
+ minikube image build -t spring-client:1.0.0 .
+ eval $(minikube docker-env)
+ docker images
+ - name: start spring-client
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-client --timeout=10s
+ kubectl port-forward svc/spring-client 8080:8080 &
+ - name: start spring-server with envoy
+ shell: bash
+ run: |
+ kubectl label namespace default istio-injection=enabled
+ sleep 5s
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ - name: test one server
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ONE_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: stop spring-server
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
+ sleep 3s
+ - name: test the number of spring-server instances changes from 1 to 0
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_ZERO_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: start spring-server with 2 replicas
+ shell: bash
+ run: |
+ kubectl apply -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
+ kubectl wait --for=condition=ready pod -l app=spring-server --timeout=10s
+ sleep 2s
+ - name: test the number of spring-server instances changes from 0 to 2
+ shell: bash
+ run: |
+ mvn test -Dxds.service.integration.test.type=DISCOVERY_TWO_SERVER_INSTANCE --file \
+ sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
+ - name: close all service
+ shell: bash
+ run: |
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
+ kubectl delete -f sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
\ No newline at end of file
diff --git a/.github/workflows/xds_service_test.yml b/.github/workflows/xds_service_test.yml
new file mode 100644
index 0000000000..b57404e41a
--- /dev/null
+++ b/.github/workflows/xds_service_test.yml
@@ -0,0 +1,88 @@
+name: xds integration test
+on:
+ push:
+ pull_request:
+ branches:
+ - '*'
+ paths:
+ - 'sermant-agentcore/**'
+ - 'sermant-integration-tests/xds-service-test/**'
+ - '.github/workflows/xds_service_test.yml'
+ - '.github/actions/common/xds-service/**'
+ - '.github/actions/scenarios/xds-service/**'
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.head_ref }}
+ cancel-in-progress: true
+jobs:
+ build-agent-and-cache:
+ name: build agent and cache
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK 8
+ uses: actions/setup-java@v3
+ with:
+ java-version: '8'
+ distribution: 'adopt'
+ cache: maven
+ - name: cache agent
+ uses: actions/cache@v3
+ with:
+ path: sermant-agent-*/
+ key: ${{ runner.os }}-agent-${{ github.run_id }}
+ - name: cache xds service package
+ uses: actions/cache@v3
+ with:
+ path: sermant-integration-tests/xds-service-test/product/
+ key: ${{ runner.os }}-xds-service-product-${{ github.run_id }}
+ - name: package agent
+ run: |
+ sed -i '/sermant-backend/d' pom.xml
+ sed -i '/sermant-injector/d' pom.xml
+ mvn package -DskipTests -Ptest --file pom.xml
+ cp -r sermant-integration-tests/xds-service-test/product/spring-client/agent sermant-integration-tests/xds-service-test/product/spring-server/
+ test-for-xds-service-discovery-onlysermant:
+ name: Test for xds service discovery with only sermant
+ runs-on: ubuntu-latest
+ needs: [build-agent-and-cache]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 100
+ - name: set java version to environment
+ run: |
+ echo "javaVersion=8" >> $GITHUB_ENV
+ - name: xds common operation
+ uses: ./.github/actions/common/xds-service
+ - name: xds service discovery
+ uses: ./.github/actions/scenarios/xds-service/xds-service-discovery/sermant-only
+ test-for-xds-service-discovery-with-server-envoy:
+ name: Test for xds service discovery with spring-server using envoy
+ runs-on: ubuntu-latest
+ needs: [build-agent-and-cache]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 100
+ - name: set java version to environment
+ run: |
+ echo "javaVersion=8" >> $GITHUB_ENV
+ - name: xds common operation
+ uses: ./.github/actions/common/xds-service
+ - name: xds service discovery
+ uses: ./.github/actions/scenarios/xds-service/xds-service-discovery/server-envoy
+ test-for-xds-service-discovery-with-client-envoy:
+ name: Test for xds service discovery with spring-client using enovy
+ runs-on: ubuntu-latest
+ needs: [build-agent-and-cache]
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 100
+ - name: set java version to environment
+ run: |
+ echo "javaVersion=8" >> $GITHUB_ENV
+ - name: xds common operation
+ uses: ./.github/actions/common/xds-service
+ - name: xds service discovery
+ uses: ./.github/actions/scenarios/xds-service/xds-service-discovery/client-envoy
\ No newline at end of file
diff --git a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties
index 65c47396b7..0e66f48135 100644
--- a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties
+++ b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties
@@ -34,6 +34,8 @@ agent.service.inject.enable=true
agent.service.dynamic.config.enable=true
# HTTP server switch
agent.service.httpserver.enable=false
+# xDS service switch
+agent.service.xds.service.enable=false
#============================= Event configuration =============================#
# Event switch
event.enable=false
@@ -91,6 +93,13 @@ gateway.nettyPort=6888
#gateway.initReconnectInternalTime=5
# Specify retreat algorithm maximum connection interval (s)
#gateway.maxReconnectInternalTime=180
+#=============================xds configuration===============================#
+# istiod control plane address, security.enable=false with 15010 port, and security.enable=true with 15012 port
+xds.config.control.plane.address=istiod.istio-system.svc:15010
+# Whether to use secure communication with the control plane
+xds.config.security.enable=false
+# service account token used for secure communication with the control plane
+xds.config.service.account.token.path=/var/run/secrets/kubernetes.io/serviceaccount/token
#=============================Metadata===============================#
# Service name for host service instance
service.meta.service=default
diff --git a/sermant-integration-tests/pom.xml b/sermant-integration-tests/pom.xml
index 361afad2a3..2883cd1e41 100644
--- a/sermant-integration-tests/pom.xml
+++ b/sermant-integration-tests/pom.xml
@@ -1,19 +1,4 @@
-
@@ -35,5 +20,6 @@
tag-transmission-test
mq-consume-prohibition-test
database-write-prohibition-test
+ xds-service-test
diff --git a/sermant-integration-tests/xds-service-test/config/plugins.yaml b/sermant-integration-tests/xds-service-test/config/plugins.yaml
new file mode 100644
index 0000000000..fa3c76d3b1
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/config/plugins.yaml
@@ -0,0 +1,2 @@
+plugins:
+ - xds-service-discovery
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/pom.xml b/sermant-integration-tests/xds-service-test/pom.xml
new file mode 100644
index 0000000000..8ebebb3432
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ sermant-integration-tests
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ xds-service-test
+ pom
+
+
+ 8
+ 8
+ 2.7.17
+
+
+
+ spring-client
+ spring-server
+ xds-service-discovery
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/product/spring-client/Dockerfile b/sermant-integration-tests/xds-service-test/product/spring-client/Dockerfile
new file mode 100644
index 0000000000..a8610d97cd
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-client/Dockerfile
@@ -0,0 +1,7 @@
+FROM openjdk:8
+WORKDIR /home
+COPY agent/ /home/agent
+COPY spring-client.jar /home/spring-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-client/start.sh b/sermant-integration-tests/xds-service-test/product/spring-client/start.sh
new file mode 100644
index 0000000000..2cde5e71e1
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-client/start.sh
@@ -0,0 +1 @@
+exec java -jar /home/spring-client.jar
diff --git a/sermant-integration-tests/xds-service-test/product/spring-server/Dockerfile b/sermant-integration-tests/xds-service-test/product/spring-server/Dockerfile
new file mode 100644
index 0000000000..36e3064789
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-server/Dockerfile
@@ -0,0 +1,7 @@
+FROM openjdk:8
+WORKDIR /home
+COPY agent/ /home/agent
+COPY spring-server.jar /home/spring-server.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-server/start.sh b/sermant-integration-tests/xds-service-test/product/spring-server/start.sh
new file mode 100644
index 0000000000..9203dd1c8d
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/product/spring-server/start.sh
@@ -0,0 +1 @@
+exec java -jar /home/spring-server.jar
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-close.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-close.yaml
new file mode 100644
index 0000000000..e02cbc040c
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-close.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: "false"
+ - name: agent_config_ignoredPrefixes
+ value: "ignore"
+ - 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
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
new file mode 100644
index 0000000000..ada30345f5
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-open.yaml
@@ -0,0 +1,45 @@
+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: agent_config_ignoredPrefixes
+ value: "ignore"
+ - 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
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-subscribe-secure.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-subscribe-secure.yaml
new file mode 100644
index 0000000000..c875a20d79
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-client-sermant-xds-subscribe-secure.yaml
@@ -0,0 +1,63 @@
+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
+ volumeMounts:
+ - name: token-volume
+ mountPath: /var/run/secrets/tokens
+ readOnly: true
+ env:
+ - name: agent_service_dynamic_config_enable
+ value: "false"
+ - name: agent_service_xds_service_enable
+ value: "true"
+ - name: agent_config_ignoredPrefixes
+ value: "ignore"
+ - name: xds_service_discovery_upstreamServiceName
+ value: "spring-server"
+ - name: xds_service_discovery_type
+ value: "subscribe"
+ - name: xds_config_control_plane_address
+ value: "istiod.istio-system.svc:15012"
+ - name: xds_config_security_enable
+ value: "true"
+ - name: xds_config_service_account_token_path
+ value: "/var/run/secrets/tokens/token"
+ - name: JAVA_TOOL_OPTIONS
+ value: "-javaagent:/home/agent/sermant-agent.jar"
+ volumes:
+ - name: token-volume
+ secret:
+ secretName: istio-test-secret
+ 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
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
new file mode 100644
index 0000000000..df6eeb1b67
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-server-2-replicas.yaml
@@ -0,0 +1,36 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-server
+spec:
+ replicas: 2
+ selector:
+ matchLabels:
+ app: spring-server
+ template:
+ metadata:
+ labels:
+ app: spring-server
+ spec:
+ containers:
+ - name: spring-server
+ image: spring-server:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-server
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8080
+ targetPort: 8080
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-server
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
new file mode 100644
index 0000000000..e10869ad20
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-server-sermant-xds-open.yaml
@@ -0,0 +1,39 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-server
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-server
+ template:
+ metadata:
+ labels:
+ app: spring-server
+ spec:
+ containers:
+ - name: spring-server
+ image: spring-server:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
+ env:
+ - name: JAVA_TOOL_OPTIONS
+ value: "-javaagent:/home/agent/sermant-agent.jar"
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-server
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8080
+ targetPort: 8080
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-server
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml b/sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
new file mode 100644
index 0000000000..952e0f5483
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/discovery/spring-server.yaml
@@ -0,0 +1,36 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: spring-server
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: spring-server
+ template:
+ metadata:
+ labels:
+ app: spring-server
+ spec:
+ containers:
+ - name: spring-server
+ image: spring-server:1.0.0
+ imagePullPolicy: Never
+ ports:
+ - containerPort: 8080
+ imagePullSecrets:
+ - name: default-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: spring-server
+spec:
+ type: ClusterIP
+ ports:
+ - port: 8080
+ targetPort: 8080
+ protocol: TCP
+ name: http
+ selector:
+ app: spring-server
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/script/istio-operator.yaml b/sermant-integration-tests/xds-service-test/script/istio-operator.yaml
new file mode 100644
index 0000000000..008ca8fbfc
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/script/istio-operator.yaml
@@ -0,0 +1,36 @@
+apiVersion: install.istio.io/v1alpha1
+kind: IstioOperator
+spec:
+ profile: default
+ components:
+ ingressGateways:
+ - name: istio-ingressgateway
+ enabled: true
+ k8s:
+ resources:
+ limits:
+ cpu: 500m
+ memory: 256Mi
+ requests:
+ cpu: 250m
+ memory: 128Mi
+ pilot:
+ enabled: true
+ k8s:
+ resources:
+ limits:
+ cpu: 500m
+ memory: 256Mi
+ requests:
+ cpu: 250m
+ memory: 128Mi
+ values:
+ global:
+ proxy:
+ resources:
+ limits:
+ cpu: 1000m
+ memory: 128Mi
+ requests:
+ cpu: 100m
+ memory: 64Mi
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/spring-client/pom.xml b/sermant-integration-tests/xds-service-test/spring-client/pom.xml
new file mode 100644
index 0000000000..7abc5e5cfd
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-client/pom.xml
@@ -0,0 +1,80 @@
+
+
+
+ xds-service-test
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ spring-client
+
+
+ 8
+ 8
+ 4.5.13
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.version}
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.version}
+
+ io.sermant.demo.spring.client.SpringClientApplication
+ spring-client
+ ${pom.basedir}/../product/spring-client
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 2.5
+ false
+
+
+ agent-clean
+ clean
+
+ clean
+
+
+
+
+ ${pom.basedir}/../product/spring-client
+
+ spring-client.jar
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientApplication.java b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientApplication.java
new file mode 100644
index 0000000000..79119502da
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientApplication.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.spring.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * SpringClientApplication
+ *
+ * @author daizhenyu
+ * @since 2024-07-02
+ **/
+@SpringBootApplication
+public class SpringClientApplication {
+ /**
+ * main
+ *
+ * @param args args
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(SpringClientApplication.class, args);
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientController.java b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientController.java
new file mode 100644
index 0000000000..b5f6579c78
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-client/src/main/java/io/sermant/demo/spring/client/SpringClientController.java
@@ -0,0 +1,99 @@
+/*
+ * 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 org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+/**
+ * controller
+ *
+ * @author daizhenyu
+ * @since 2024-07-02
+ **/
+@RestController
+public class SpringClientController {
+ private static final String HELLO_METHOD_PATH = "/hello";
+
+ private static final int SUCCESS_CODE = 200;
+
+ private static final int MAX_TOTAL = 200;
+
+ private static final int MAX_PER_ROUTE = 20;
+
+ private static final int CONNECT_TIMEOUT = 200;
+
+ private static final int SOCKET_TIMEOUT = 200;
+
+ private static final int RETRY_COUNT = 3;
+
+ private static CloseableHttpClient httpClient;
+
+ static {
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+ connectionManager.setMaxTotal(MAX_TOTAL);
+ connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
+
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(CONNECT_TIMEOUT)
+ .setSocketTimeout(SOCKET_TIMEOUT)
+ .build();
+
+ httpClient = HttpClients.custom()
+ .setConnectionManager(connectionManager)
+ .setDefaultRequestConfig(requestConfig)
+ .setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_COUNT, true))
+ .build();
+ }
+
+ /**
+ * call upstream service
+ *
+ * @param address address
+ * @return result
+ */
+ @RequestMapping("hello")
+ public String hello(String address) {
+ StringBuilder urlBuilder = new StringBuilder();
+ urlBuilder.append("http://");
+ urlBuilder.append(address);
+ urlBuilder.append(HELLO_METHOD_PATH);
+ return httpGet(urlBuilder.toString());
+ }
+
+ private String httpGet(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+ if (response.getStatusLine().getStatusCode() == SUCCESS_CODE) {
+ return EntityUtils.toString(response.getEntity());
+ }
+ return "";
+ } catch (IOException e) {
+ return "";
+ }
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-server/pom.xml b/sermant-integration-tests/xds-service-test/spring-server/pom.xml
new file mode 100644
index 0000000000..07385222ba
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-server/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ xds-service-test
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ spring-server
+
+
+ 8
+ 8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.version}
+
+ io.sermant.demo.spring.server.SpringServerApplication
+ spring-server
+ ${pom.basedir}/../product/spring-server
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 2.5
+ false
+
+
+ agent-clean
+ clean
+
+ clean
+
+
+
+
+ ${pom.basedir}/../product/spring-server
+
+ spring-server.jar
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerApplication.java b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerApplication.java
new file mode 100644
index 0000000000..2be2bc65af
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerApplication.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.spring.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * SpringServerApplication
+ *
+ * @author daizhenyu
+ * @since 2024-07-02
+ **/
+@SpringBootApplication
+public class SpringServerApplication {
+ /**
+ * main
+ *
+ * @param args args
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(SpringServerApplication.class, args);
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerController.java b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerController.java
new file mode 100644
index 0000000000..2f2f37303d
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/spring-server/src/main/java/io/sermant/demo/spring/server/SpringServerController.java
@@ -0,0 +1,39 @@
+/*
+ * 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.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * controller
+ *
+ * @author daizhenyu
+ * @since 2024-07-02
+ **/
+@RestController
+public class SpringServerController {
+ /**
+ * say hello
+ *
+ * @return result
+ */
+ @RequestMapping("hello")
+ public String sayHello() {
+ return "hello";
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/config/config.yaml b/sermant-integration-tests/xds-service-test/xds-service-discovery/config/config.yaml
new file mode 100644
index 0000000000..f7f8e2922b
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/config/config.yaml
@@ -0,0 +1,4 @@
+xds.service.discovery:
+ enabled: true
+ upstreamServiceName: spring-server
+ type: get
\ No newline at end of file
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
new file mode 100644
index 0000000000..9bf2005a07
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/pom.xml
@@ -0,0 +1,256 @@
+
+
+
+ xds-service-test
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ xds-service-discovery
+ pom
+
+
+ ${pom.basedir}/../product/spring-client
+ xds-service-discovery
+ ${output.basedir}/agent/pluginPackage
+ undefined
+ ${package.plugin.dir}/${package.plugin.name}/${package.plugin.type}
+ ${project.version}
+ ${pom.basedir}/../../../sermant-agent-${project.version}/agent
+ ${package.plugin.dir}/${package.plugin.name}/config
+ ${pom.basedir}/../config
+ ../config
+ 8
+ 8
+
+
+
+
+ io.sermant
+ sermant-agentcore-core
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.1.0
+ false
+
+
+ copy-config
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/config
+ true
+
+
+ ${sermant.source.dir}/config
+
+ bootstrap.properties
+ config.properties
+ logback.xml
+
+
+
+ ${sermant.plugins.config.dir}
+
+ plugins.yaml
+
+
+
+
+
+
+ copy-common
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/common
+ true
+
+
+ ${sermant.source.dir}/common
+
+ *
+
+
+
+
+
+
+ copy-core
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/core
+ true
+
+
+ ${sermant.source.dir}/core
+
+ *
+
+
+
+
+
+
+ copy-god
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/god
+ true
+
+
+ ${sermant.source.dir}/god
+
+ *
+
+
+
+
+
+
+ copy-implement
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent/implement
+ true
+
+
+ ${sermant.source.dir}/implement
+
+ *
+
+
+
+
+
+
+ copy-agent
+ package
+
+ copy-resources
+
+
+ ${output.basedir}/agent
+ true
+
+
+ ${sermant.source.dir}
+
+ sermant-agent.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ 2.5
+ false
+
+
+ agent-clean
+ clean
+
+ clean
+
+
+
+
+ ${output.basedir}/agent
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ ${package.plugin.name}
+ ${package.plugin.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ package
+
+ copy-resources
+
+
+ ${config.output.dir}
+ true
+
+
+ ${plugin.config.dir}
+
+ config.yaml
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+
+
+ ${package.output.dir}/${project.artifactId}-${project.version}.jar
+
+
+
+
+
+
+
+
+
+
+ xds-service-discovery-plugin
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/pom.xml b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/pom.xml
new file mode 100644
index 0000000000..fd926cdedf
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/pom.xml
@@ -0,0 +1,47 @@
+
+
+
+ xds-service-discovery
+ io.sermant.integration
+ 1.0.0
+
+
+ 4.0.0
+ xds-service-discovery-plugin
+
+
+ ${pom.basedir}/../../product/spring-client
+ xds-service-discovery
+ plugin
+ false
+ 8
+ 8
+
+
+
+
+ io.sermant
+ sermant-agentcore-core
+ ${project.version}
+ provided
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.1.0
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateConfig.java b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateConfig.java
new file mode 100644
index 0000000000..c5651a5e85
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateConfig.java
@@ -0,0 +1,63 @@
+/*
+ * 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.discovery;
+
+import io.sermant.core.config.common.ConfigFieldKey;
+import io.sermant.core.config.common.ConfigTypeKey;
+import io.sermant.core.plugin.config.PluginConfig;
+
+/**
+ * config
+ *
+ * @author daizhenyu
+ * @since 2024-07-02
+ **/
+@ConfigTypeKey("xds.service.discovery")
+public class TemplateConfig implements PluginConfig {
+ @ConfigFieldKey("upstreamServiceName")
+ private String upstreamServiceName;
+
+ @ConfigFieldKey("type")
+ private String type;
+
+ @ConfigFieldKey("enabled")
+ private boolean enabled;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getUpstreamServiceName() {
+ return upstreamServiceName;
+ }
+
+ public void setUpstreamServiceName(String upstreamServiceName) {
+ this.upstreamServiceName = upstreamServiceName;
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateDeclarer.java b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateDeclarer.java
new file mode 100644
index 0000000000..a0833b5a5b
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateDeclarer.java
@@ -0,0 +1,49 @@
+/*
+ * 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.discovery;
+
+import io.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer;
+import io.sermant.core.plugin.agent.declarer.InterceptDeclarer;
+import io.sermant.core.plugin.agent.matcher.ClassMatcher;
+import io.sermant.core.plugin.agent.matcher.MethodMatcher;
+
+/**
+ * template bytecode enhancement declarer
+ *
+ * @author daizhnyu
+ * @since 2024-07-05
+ */
+public class TemplateDeclarer extends AbstractPluginDeclarer {
+ @Override
+ public ClassMatcher getClassMatcher() {
+ // Matches the class to be intercepted by fully qualified name
+ return ClassMatcher.nameEquals("io.sermant.demo.spring.client.SpringClientController");
+ }
+
+ @Override
+ public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) {
+ return new InterceptDeclarer[]{
+ // matches the method to be intercepted by name
+ InterceptDeclarer.build(MethodMatcher.nameEquals("hello"), new TemplateInterceptor())
+ };
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return super.isEnabled();
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateInterceptor.java b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateInterceptor.java
new file mode 100644
index 0000000000..6c2916ae72
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/java/io/sermant/xds/service/discovery/TemplateInterceptor.java
@@ -0,0 +1,110 @@
+/*
+ * 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.discovery;
+
+import io.sermant.core.plugin.agent.entity.ExecuteContext;
+import io.sermant.core.plugin.agent.interceptor.Interceptor;
+import io.sermant.core.plugin.config.PluginConfigManager;
+import io.sermant.core.service.ServiceManager;
+import io.sermant.core.service.xds.XdsCoreService;
+import io.sermant.core.service.xds.XdsServiceDiscovery;
+import io.sermant.core.service.xds.entity.ServiceInstance;
+import io.sermant.core.service.xds.listener.XdsServiceDiscoveryListener;
+import io.sermant.core.utils.CollectionUtils;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * interceptor
+ *
+ * @author daizhenyu
+ * @since 2024-07-05
+ **/
+public class TemplateInterceptor implements Interceptor {
+ private final XdsServiceDiscovery xdsServiceDiscovery;
+
+ private final TemplateConfig templateConfig;
+
+ private List instanceList = new ArrayList<>();
+
+ private SecureRandom secureRandom = new SecureRandom();
+
+ /**
+ * constructor
+ */
+ public TemplateInterceptor() {
+ xdsServiceDiscovery = ServiceManager.getService(XdsCoreService.class)
+ .getXdsServiceDiscovery();
+ templateConfig = PluginConfigManager.getPluginConfig(TemplateConfig.class);
+ if (templateConfig.getType().equals("subscribe")) {
+ xdsServiceDiscovery.subscribeServiceInstance(templateConfig.getUpstreamServiceName(),
+ new XdsServiceDiscoveryListener() {
+ @Override
+ public void process(Set instances) {
+ instanceList = new ArrayList<>(instances);
+ }
+ });
+ }
+ }
+
+ @Override
+ public ExecuteContext before(ExecuteContext context) {
+ if (!templateConfig.isEnabled()) {
+ return context;
+ }
+ if (templateConfig.getType().equals("get")) {
+ instanceList = new ArrayList<>(xdsServiceDiscovery
+ .getServiceInstance(templateConfig.getUpstreamServiceName()));
+ }
+
+ context.setLocalFieldValue("serviceCount", instanceList.size());
+ if (CollectionUtils.isEmpty(instanceList)) {
+ return context;
+ }
+ int randomIndex = secureRandom.nextInt(instanceList.size());
+ ServiceInstance instance = instanceList.get(randomIndex);
+ Object[] arguments = context.getArguments();
+ arguments[0] = buildUrl(instance);
+ return context;
+ }
+
+ @Override
+ public ExecuteContext after(ExecuteContext context) {
+ if (!templateConfig.isEnabled()) {
+ return context;
+ }
+ String oldResult = (String) context.getResult();
+ context.changeResult(oldResult + "-" + context.getLocalFieldValue("serviceCount"));
+ return context;
+ }
+
+ @Override
+ public ExecuteContext onThrow(ExecuteContext context) {
+ return context;
+ }
+
+ private String buildUrl(ServiceInstance instance) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(instance.getHost());
+ builder.append(":");
+ builder.append(instance.getPort());
+ return builder.toString();
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer
new file mode 100644
index 0000000000..2631c72a90
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+io.sermant.xds.service.discovery.TemplateDeclarer
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig
new file mode 100644
index 0000000000..3ff82c5e7b
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-discovery/xds-service-discovery-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.config.PluginConfig
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+io.sermant.xds.service.discovery.TemplateConfig
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml b/sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
new file mode 100644
index 0000000000..2eb5d5770b
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-integration-test/pom.xml
@@ -0,0 +1,69 @@
+
+
+
+ xds-service-test
+ io.sermant.integration
+ 1.0.0
+
+ 4.0.0
+
+ xds-service-integration-test
+
+
+ 8
+ 8
+ 4.5.13
+ 1.2
+ 3.0.0
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpclient4x.version}
+ test
+
+
+ ch.qos.logback
+ logback-core
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+ org.slf4j
+ slf4j-api
+ test
+
+
+ commons-logging
+ commons-logging
+ ${commons-log.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surefire.plugin.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/discovery/XdsServiceDiscoveryTest.java b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/discovery/XdsServiceDiscoveryTest.java
new file mode 100644
index 0000000000..05bdd4efa3
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/discovery/XdsServiceDiscoveryTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.discovery;
+
+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 service discovery test
+ *
+ * @author daizhenyu
+ * @since 2024-07-15
+ **/
+public class XdsServiceDiscoveryTest {
+ /**
+ * one server instance
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "DISCOVERY_ONE_SERVER_INSTANCE")
+ public void testDiscoveryWithOneServerInstance() {
+ String[] resultStrings = splitResult(HttpRequestUtils.doGet("http://127.0.0.1:8080/hello"));
+ Assertions.assertEquals(2, resultStrings.length, "The returned result format is incorrect.");
+ Assertions.assertEquals("hello", resultStrings[0]);
+ Assertions.assertEquals("1", resultStrings[1]);
+ }
+
+ /**
+ * zero server instance
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "DISCOVERY_ZERO_SERVER_INSTANCE")
+ public void testDiscoveryWithZeroServerInstance() {
+ String[] resultStrings = splitResult(HttpRequestUtils.doGet("http://127.0.0.1:8080/hello"));
+ Assertions.assertEquals(2, resultStrings.length, "The returned result format is incorrect.");
+ Assertions.assertEquals("0", resultStrings[1]);
+ }
+
+ /**
+ * two server instance
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "DISCOVERY_TWO_SERVER_INSTANCE")
+ public void testDiscoveryWithTwoServerInstance() {
+ String[] resultStrings = splitResult(HttpRequestUtils.doGet("http://127.0.0.1:8080/hello"));
+ Assertions.assertEquals(2, resultStrings.length, "The returned result format is incorrect.");
+ Assertions.assertEquals("hello", resultStrings[0]);
+ Assertions.assertEquals("2", resultStrings[1]);
+ }
+
+ /**
+ * secret
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "SECRET")
+ public void testDiscoveryWithSecret() {
+ String[] resultStrings = splitResult(HttpRequestUtils.doGet("http://127.0.0.1:8080/hello"));
+ Assertions.assertEquals(2, resultStrings.length, "The returned result format is incorrect.");
+ Assertions.assertEquals("hello", resultStrings[0]);
+ Assertions.assertEquals("2", resultStrings[1]);
+ }
+
+ /**
+ * subscribe to get service instance
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches = "DISCOVERY_SUBSCRIBE")
+ public void testDiscoveryWithSubscribe() {
+ String[] resultStrings = splitResult(HttpRequestUtils.doGet("http://127.0.0.1:8080/hello"));
+ Assertions.assertEquals(2, resultStrings.length, "The returned result format is incorrect.");
+ Assertions.assertEquals("hello", resultStrings[0]);
+ Assertions.assertEquals("2", resultStrings[1]);
+ }
+
+ /**
+ * one server instance with client using envoy
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches =
+ "DISCOVERY_ONE_SERVER_INSTANCE_ENVOY")
+ public void testDiscoveryWithClientUsingEnvoyAndOneInstance() {
+ Assertions.assertEquals("hello", HttpRequestUtils.doGet("http://127.0.0.1:8080/hello?address=spring-server"
+ + ".default.svc.cluster.local:8080"));
+ }
+
+ /**
+ * zero server instance with client using envoy
+ */
+ @Test
+ @EnabledIfSystemProperty(named = "xds.service.integration.test.type", matches =
+ "DISCOVERY_ZERO_SERVER_INSTANCE_ENVOY")
+ public void testDiscoveryWithClientUsingEnvoyAndZeroInstance() {
+ Assertions.assertEquals("", HttpRequestUtils.doGet("http://127.0.0.1:8080/hello?address=spring-server"
+ + ".default.svc.cluster.local:8080"));
+ }
+
+ private String[] splitResult(String result) {
+ Assertions.assertNotNull(result, "The returned result is null.");
+ return result.split("-");
+ }
+}
diff --git a/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/utils/HttpRequestUtils.java b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/utils/HttpRequestUtils.java
new file mode 100644
index 0000000000..a36bdf5547
--- /dev/null
+++ b/sermant-integration-tests/xds-service-test/xds-service-integration-test/src/test/java/io/sermant/xds/service/utils/HttpRequestUtils.java
@@ -0,0 +1,68 @@
+/*
+ * 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.utils;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+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.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * http utils
+ *
+ * @author daizhenyu
+ * @since 2024-01-08
+ **/
+public class HttpRequestUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpRequestUtils.class);
+
+ private static final int SUCCESS_CODE = 200;
+
+ private HttpRequestUtils() {
+ }
+
+ /**
+ * http get
+ *
+ * @param url http url
+ * @return response body
+ */
+ public static String doGet(String url) {
+ try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ RequestConfig requestConfig = RequestConfig.custom()
+ .build();
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.setConfig(requestConfig);
+ try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+ if (response.getStatusLine().getStatusCode() == SUCCESS_CODE) {
+ return EntityUtils.toString(response.getEntity());
+ }
+ LOGGER.info("Request error, the message is: {}", EntityUtils.toString(response.getEntity()));
+ return "";
+ }
+ } catch (IOException e) {
+ LOGGER.info("Request exception, the message is: {}", e.getMessage());
+ return "";
+ }
+ }
+}