This is an automated email from the ASF dual-hosted git repository.
yuluo-yx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 48120c1366 Stabilize k8s ingress startup healthchecks (#6361)
48120c1366 is described below
commit 48120c13668150b9e9b4c9f0786f671451d58ecc
Author: aias00 <[email protected]>
AuthorDate: Mon Jun 8 16:55:11 2026 +0800
Stabilize k8s ingress startup healthchecks (#6361)
* goalx: snapshot before shenyu-analysis
* chore(ci): optimize workflow build cache and mvnd parallelism
* Stabilize integration startup healthchecks
The grpc ingress CI failure timed out while a healthcheck curl waited on a
newly created kind deployment. The workflow now waits for shenyu-ingress
deployments to roll out before HTTP probing, bounds each curl attempt, and
preserves cluster diagnostics even when startup fails.
The HTTP integration workflow also exposed a Sentinel fallback flake where
a POST request relied on an external redirect fallback and hung until the
180-second client timeout. The Sentinel test now uses internal fallback
dispatch, and the example accepted endpoint supports POST for that fallback
path.
Constraint: GitHub-hosted kind and docker-compose startup can vary with
runner load and JVM boot time
Rejected: Only increase the Actions timeout | leaves a hanging curl and no
readiness boundary
Rejected: Only add diagnostic logging | improves evidence but still allows
avoidable flakes
Rejected: Keep Sentinel fallback as a client-followed 302 redirect | keeps
the flaky non-2xx fallback boundary
Confidence: medium
Scope-risk: moderate
Directive: Keep k8s ingress healthchecks bounded and rollout-aware; do not
reintroduce unbounded curl startup waits
Tested: bash -n for helper and ingress healthcheck scripts
Tested: git diff --check
Tested: local fake kubectl plus HTTP 200 success path
Tested: local fake kubectl plus unreachable service failure path
Tested: ./mvnw -f shenyu-examples/shenyu-examples-http/pom.xml -DskipTests
-Dmaven.javadoc.skip=true compile
Tested: ./mvnw -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-http -am -DskipTests -Pit -Dmaven.javadoc.skip=true
test-compile
Not-tested: full GitHub Actions kind matrix run
Not-tested: local docker-compose shenyu-integrated-test-http runtime
* Keep ingress healthchecks compatible with CI kubectl
The k8s ingress startup helper must run on the kubectl v1.24 binary
installed by the workflow. That binary rejects rollout status with deployment
--all, so enumerate deployments first and wait on each named resource becoming
Available before starting HTTP health probes.
Constraint: CI installs kubectl v1.24.14
Rejected: Keep rollout status deployment --all | rejected by the linked PR
6361 CI job with unknown flag: --all
Confidence: high
Scope-risk: narrow
Directive: Keep this helper compatible with the kubectl version pinned in
integrated-test-k8s-ingress.yml
Tested: bash -n for helper and ingress healthcheck scripts
Tested: git diff --check for helper
Tested: fake kubectl/curl success, failed service, and no-deployment helper
paths
* Stop k8s example probes from killing ready services
The k8s ingress apache-dubbo job reached the new bounded healthcheck path,
then failed because the example service nodePort stayed unreachable. The
diagnostic dump showed kubelet restarting the Dubbo example after liveness
probes hit localhost:8081, while the application, containerPort, and service
all use 8011. The SOFA example had the same stale probe port against an
application that serves health on 28011, so align both probes with their real
server ports.
Constraint: The k8s ingress workflow now waits on deployment availability
and then verifies the example NodePort health endpoints
Rejected: Increase healthcheck retries only | would leave kubelet
repeatedly restarting a healthy app due to the wrong probe port
Confidence: high
Scope-risk: narrow
Directive: Keep k8s liveness probe ports aligned with each example
application's server.port and service targetPort
Tested: git diff --check for changed manifests
Tested: Ruby YAML parser for changed manifests
Not-tested: local kind run; CI job is the available end-to-end validation
* Expose ingress healthcheck node ports in kind
The apache-dubbo ingress job now reaches healthy pods with zero restarts,
but the host healthcheck still curls localhost:31187. In kind, localhost access
requires explicit extraPortMappings; only the ingress controller port 30095 was
mapped, so service NodePorts such as 31187 were unreachable from the runner
even when Kubernetes reported the service and pod ready.
Map each active ingress services.list localhost port through the case kind
config. Also align the latent SOFA services.list port with its actual example
service NodePort and map it for future activation.
Constraint: GitHub Actions probes services from the host runner via
localhost against a kind cluster
Rejected: Probe only Kubernetes deployment readiness | misses host-to-kind
NodePort reachability that the tests depend on
Rejected: Increase curl retries | localhost can never reach unmapped kind
NodePorts
Confidence: high
Scope-risk: narrow
Directive: Any localhost port added to an ingress services.list must also
be present as a kind extraPortMappings hostPort
Tested: services.list localhost ports match kind hostPort mappings for
http, grpc, websocket, apache-dubbo, and sofa
Tested: Ruby YAML parser for changed kind configs and related manifests
Tested: git diff --check for changed files
Not-tested: local kind run; CI job is the available end-to-end validation
* Align grpc ingress health probes with app path
After NodePort mappings were fixed, apache-dubbo passed and the remaining
k8s ingress failure moved to grpc. The grpc example was restarted by liveness
because the probe hit /actuator/health while the app serves under the /grpc
context path. The host healthcheck also probed NodePort 31184, which maps
service port 8080, while the application HTTP server is 55290 exposed as
NodePort 31183.
Use the same health endpoint shape as the docker-compose grpc integration
check: /grpc/actuator/health on port 55290, exposed to the runner as
localhost:31183 in kind.
Constraint: shenyu-examples-grpc sets server.servlet.context-path=/grpc and
server.port=55290
Rejected: Keep probing 31184 | that NodePort maps port 8080 rather than the
app health server
Rejected: Increase retries | the liveness path was wrong and caused restarts
Confidence: high
Scope-risk: narrow
Directive: Keep grpc k8s health probes consistent with
server.servlet.context-path and service NodePort 31183
Tested: services.list localhost ports match kind hostPort mappings for all
ingress cases
Tested: Ruby YAML parser for changed manifests
Tested: git diff --check
Not-tested: local kind run; CI job is the available end-to-end validation
* Use stable websocket ingress health endpoint
The websocket ingress preflight polled a historical HTTP path that is not
implemented by the annotation websocket example. CI reached the service but
received repeated 404 responses, so the cluster startup step failed before the
real websocket test could run.
Use the actuator health endpoint that the same example already exposes for
Kubernetes liveness, and give the example a longer initial liveness delay to
avoid startup probe restarts observed in CI diagnostics.
Constraint: The preflight check must use an HTTP URL, while the actual
integration coverage remains the ws://localhost:30095/myWs test.
Rejected: Add a dummy HTTP handler for /new/feature/get/mapping/path | it
would preserve a stale health endpoint unrelated to websocket coverage.
Confidence: high
Scope-risk: narrow
Tested: git diff --check
Tested: Ruby YAML parse for websocket Kubernetes manifest
Tested: kind hostPort mapping consistency check for ingress service lists
Tested: ./mvnw -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-k8s-ingress-websocket -am -DskipTests -Pit
-Dmaven.javadoc.skip=true test-compile
Not-tested: full Kubernetes ingress websocket workflow locally; requires CI
kind/docker environment
* Avoid invalid websocket ingress HTTP health probe
The websocket ingress preflight still failed after moving to the example
actuator endpoint because the service is registered in the ingress controller
as a websocket upstream. HTTP actuator probes against that path consistently
returned 404, while the actual integration test only needs the shenyu ingress
controller on port 30095 and then exercises ws://localhost:30095/myWs.
Use the ingress controller's own actuator health endpoint for websocket
preflight, remove the unused host mapping for the example NodePort, and add a
startup probe so the example liveness check does not restart the app during
slow startup.
Constraint: Websocket ingress health preflight must not depend on an HTTP
request flowing through a websocket upstream.
Rejected: Keep probing localhost:31188 | CI showed it returns 404 after
websocket upstream registration and does not represent the test path.
Confidence: high
Scope-risk: narrow
Tested: git diff --check
Tested: Ruby YAML parse for websocket Kubernetes manifest and kind config
Tested: kind hostPort mapping consistency check for ingress service lists
Tested: ./mvnw -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-k8s-ingress-websocket -am -DskipTests -Pit
-Dmaven.javadoc.skip=true test-compile
Not-tested: full Kubernetes ingress websocket workflow locally; requires CI
kind/docker environment
* Wait for ingress backend deployments before tests
The websocket ingress healthcheck now avoids probing the websocket upstream
with an HTTP health path, which exposed a race where the CI test could start
while the default namespace example deployment was still becoming available. In
that state the ingress controller can observe empty websocket rule handles and
the integration test fails even though gateway health passed. The shared helper
now waits for deployments in both shenyu-ingress and default before polling
service endpoints.\n\ [...]
* Allow optional ingress backend namespaces
The apache-dubbo ingress example deploys all workloads in shenyu-ingress,
so requiring default namespace deployments made the shared readiness helper
fail before it could poll the actual health endpoints. The helper now treats
shenyu-ingress as required and default as optional, preserving the websocket
backend readiness gate without breaking cases that do not use
default.\n\nConstraint: Ingress examples do not all use the same namespace
layout.\nRejected: Remove default namespace wait [...]
* Expose websocket example actuator health
The websocket ingress example deployment probes /actuator/health on port
8001, but the annotation websocket example did not include Spring Boot
Actuator. In CI the pod stayed 0/1 Available for ten minutes, restarted after
startupProbe failures, and blocked the shared ingress healthcheck. Adding the
actuator starter makes the existing Kubernetes startup and liveness probes
target a real endpoint.\n\nConstraint: The k8s manifest already uses
/actuator/health as the readiness signal.\nRe [...]
* Refresh websocket ingress upstreams from endpoints
The endpoint reconciler only looked up divide selectors for services mapped
from an ingress. Websocket ingress services therefore had no matching selector
ids, which produced addAll(null) NPEs and left the websocket selector handle
empty after the backend endpoint became ready. The reconciler now skips missing
selector caches and updates both divide and websocket selectors with
plugin-appropriate upstream handles.\n\nConstraint: Kubernetes endpoint events
can arrive after ingress pars [...]
* Use real sentinel fallback in HTTP integration test
The HTTP integrated matrix exposed that the sentinel fallback test was
using an upstream /http route as a fallback: target. Fallback dispatch is
gateway-local, so the test could return a backend 404 instead of proving the
sentinel fallback path ran.
This points the test at the existing /fallback/sentinel controller and
asserts the sentinel fallback enum code and message so a plain rate-limit
response cannot satisfy the fallback URI case.
Constraint: fallback: URIs are handled by the gateway DispatcherHandler,
not as a fresh divide upstream request
Rejected: Keep /http/test/request/accepted | CI proved it can resolve as a
missing backend static resource
Confidence: high
Scope-risk: narrow
Tested: ./mvnw -B -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-http -am -DskipTests -Dmaven.javadoc.skip=true
test-compile
Tested: git diff --check
Not-tested: Full docker-compose HTTP integrated test locally; CI owns the
full runtime environment
* Allow fallback endpoints to preserve request methods
Sentinel fallback dispatch preserves the original request method. The HTTP
integrated test uses POST, so the gateway-local /fallback/sentinel endpoint
returned 405 while the fallback path itself was correct.
The default fallback endpoints are fallback targets rather than public
method-specific APIs, so they now accept any method. A POST regression test
covers the sentinel path that failed in CI.
Constraint: FallbackHandler mutates only the URI and keeps the original
HTTP method
Rejected: Change the sentinel test back to a 429-only assertion | it would
not prove fallbackUri dispatch and could mask ordinary rate limiting
Confidence: high
Scope-risk: narrow
Tested: ./mvnw -B -pl shenyu-web -DskipITs -DskipTests=false
-Dtest=DefaultFallbackControllerTest -Dmaven.javadoc.skip=true test
Tested: ./mvnw -B -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-http -am -DskipTests -Dmaven.javadoc.skip=true
test-compile
Tested: git diff --check
Not-tested: Full docker-compose HTTP integrated test locally; CI owns the
full runtime environment
* Retry docker base image pulls in integration CI
The integration workflow removes runner image caches to free disk, then
docker-maven-plugin pulls Docker Hub base images during a large Maven build.
Two consecutive runs failed before tests because alpine:latest and centos:7
timed out from registry-1.docker.io.
Pre-pulling the admin/bootstrap base images with retries warms the local
Docker cache before Maven starts, reducing transient registry failures without
changing the build artifacts.
Constraint: it matrix jobs build docker images from Docker Hub bases after
deleting runner caches
Rejected: Only rerun the failed workflow | repeated reruns hit the same
registry timeout and leave the CI easy to fail
Confidence: medium
Scope-risk: narrow
Tested: ruby -e 'require "yaml";
YAML.load_file(".github/workflows/integrated-test.yml")'
Tested: git diff --check
Not-tested: Full GitHub Actions workflow locally
* Assert sentinel fallback response shape directly
CI proved the sentinel fallback path now returns the intended fallback
response, but the integration test deserialized it into ResultBean. That DTO
uses msg while the fallback response exposes message, so the behavior was
correct but the assertion read the wrong field.
The test now reads the fallback response as a map and asserts the actual
code/message fields emitted by the fallback endpoint.
Constraint: ShenyuResultWrap fallback responses use message, while
integrated-test ResultBean uses msg
Rejected: Change ResultBean or fallback response shape | broader contract
change not needed for this test
Confidence: high
Scope-risk: narrow
Tested: ./mvnw -B -f shenyu-integrated-test/pom.xml -pl
shenyu-integrated-test-http -am -DskipTests -Dmaven.javadoc.skip=true
test-compile
Tested: git diff --check
Not-tested: Full docker-compose HTTP integrated test locally; CI owns the
full runtime environment
* Retry transient CI dependency downloads
GitHub Actions runs failed before project code executed when Temurin JDK
and k3s binaries returned transient 504/download errors. Add an in-repo
setup-java retry wrapper for observed setup-java callers and retry k3s
installation in the k8s examples workflow.
Constraint: The failures happened before Maven or integration tests could
run, so test-code changes cannot address them.
Rejected: Only rerun failed jobs | leaves the same external download point
fragile on the next PR run
Confidence: medium
Scope-risk: moderate
Directive: Keep workflow download retries close to the failing external
install step so real build/test failures remain visible.
Tested: Ruby YAML parse for changed workflow/action files; git diff --check
Not-tested: actionlint is not installed locally; full GitHub Actions
validation requires PR runs
* Apply Java setup retries across CI workflows
A later e2e job failed at the same pre-test Temurin download point as ci
and it. Route the remaining workflow setup-java calls through the shared retry
wrapper so transient GitHub release 504s do not fail unrelated test lanes
before project code runs.
Constraint: GitHub-hosted runners download some requested JDKs from GitHub
releases at job runtime.
Rejected: Patch only the failed e2e job | leaves CodeQL, it-k8s, and
publish workflows with the same single-attempt setup path
Confidence: medium
Scope-risk: moderate
Directive: Keep actions/setup-java@v4 usage centralized in
actions/setup-java-with-retry unless a workflow has a documented reason to
bypass retries.
Tested: Ruby YAML parse for changed workflows and retry action; git diff
--check; rg confirms no direct workflow setup-java@v4 uses remain
Not-tested: actionlint is not installed locally; full workflow execution
requires GitHub Actions
---
.github/workflows/ci.yml | 2 +-
.github/workflows/codeql-analysis.yml | 2 +-
.github/workflows/docker-publish-dockerhub.yml | 2 +-
.github/workflows/docker-publish.yml | 2 +-
.github/workflows/e2e-k8s.yml | 6 +-
.github/workflows/integrated-test-k8s-ingress.yml | 102 ++++++--------------
.github/workflows/integrated-test.yml | 86 ++++++++---------
.github/workflows/k8s-examples-http.yml | 68 ++++++-------
actions/setup-java-with-retry/action.yml | 63 +++++++++++++
.../k8s/shenyu-examples-dubbo.yml | 2 +-
.../k8s/shenyu-examples-grpc.yml | 2 +-
.../http/controller/HttpTestController.java | 11 +++
.../k8s/shenyu-examples-sofa.yml | 2 +-
.../shenyu-example-spring-annotation-websocket.yml | 11 ++-
.../pom.xml | 4 +
shenyu-integrated-test/k8s-ingress-healthcheck.sh | 77 +++++++++++++++
.../test/http/combination/SentinelPluginTest.java | 9 +-
.../deploy/kind-config.yaml | 3 +
.../script/healthcheck.sh | 18 +---
.../deploy/kind-config.yaml | 3 +
.../script/healthcheck.sh | 18 +---
.../script/services.list | 2 +-
.../deploy/kind-config.yaml | 3 +
.../script/healthcheck.sh | 18 +---
.../deploy/kind-config.yaml | 3 +
.../script/services.list | 2 +-
.../script/healthcheck.sh | 18 +---
.../script/services.list | 2 +-
.../shenyu/k8s/reconciler/EndpointsReconciler.java | 81 +++++++++++-----
.../apache/shenyu/k8s/EndpointsReconcilerTest.java | 105 +++++++++++++++++++++
.../web/fallback/DefaultFallbackController.java | 9 +-
.../fallback/DefaultFallbackControllerTest.java | 7 ++
32 files changed, 473 insertions(+), 270 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0da28afeac..15acf77117 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -89,7 +89,7 @@ jobs:
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- - uses: actions/setup-java@v4
+ - uses: ./actions/setup-java-with-retry
if: steps.filter.outputs.code == 'true'
with:
java-version: ${{ matrix.java }}
diff --git a/.github/workflows/codeql-analysis.yml
b/.github/workflows/codeql-analysis.yml
index 2e835dcbbe..510fbea7a5 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
with:
submodules: true
- name: Set up JDK 17
- uses: actions/setup-java@v4
+ uses: ./actions/setup-java-with-retry
with:
java-version: 17
distribution: 'temurin'
diff --git a/.github/workflows/docker-publish-dockerhub.yml
b/.github/workflows/docker-publish-dockerhub.yml
index 7ca7a63fad..525b13d503 100644
--- a/.github/workflows/docker-publish-dockerhub.yml
+++ b/.github/workflows/docker-publish-dockerhub.yml
@@ -63,7 +63,7 @@ jobs:
restore-keys: |
${{ runner.os }}-maven-
- - uses: actions/setup-java@v4
+ - uses: ./actions/setup-java-with-retry
with:
java-version: 17
distribution: "temurin"
diff --git a/.github/workflows/docker-publish.yml
b/.github/workflows/docker-publish.yml
index e50138fb20..969b39e596 100644
--- a/.github/workflows/docker-publish.yml
+++ b/.github/workflows/docker-publish.yml
@@ -67,7 +67,7 @@ jobs:
restore-keys: |
${{ runner.os }}-maven-
- - uses: actions/setup-java@v4
+ - uses: ./actions/setup-java-with-retry
with:
java-version: 17
distribution: "temurin"
diff --git a/.github/workflows/e2e-k8s.yml b/.github/workflows/e2e-k8s.yml
index b9451ecff7..10c7f64e0e 100644
--- a/.github/workflows/e2e-k8s.yml
+++ b/.github/workflows/e2e-k8s.yml
@@ -79,7 +79,7 @@ jobs:
df --human-readable
- name: Set up JDK 17 for Building ShenYu
- uses: actions/setup-java@v4
+ uses: ./actions/setup-java-with-retry
with:
java-version: "17"
distribution: "temurin"
@@ -191,7 +191,7 @@ jobs:
# cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
- name: Set up JDK 17 for Building ShenYu
- uses: actions/setup-java@v4
+ uses: ./actions/setup-java-with-retry
with:
java-version: "17"
distribution: "temurin"
@@ -288,7 +288,7 @@ jobs:
# cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
- name: Set up JDK 17 for Building ShenYu
- uses: actions/setup-java@v4
+ uses: ./actions/setup-java-with-retry
with:
java-version: "17"
distribution: "temurin"
diff --git a/.github/workflows/integrated-test-k8s-ingress.yml
b/.github/workflows/integrated-test-k8s-ingress.yml
index cb8fdb08a8..763d923da6 100644
--- a/.github/workflows/integrated-test-k8s-ingress.yml
+++ b/.github/workflows/integrated-test-k8s-ingress.yml
@@ -67,20 +67,14 @@ jobs:
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- - name: Cache Maven Repos
- if: steps.filter.outputs.k8s-ingress == 'true'
- uses: actions/cache@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
-
- - uses: actions/setup-java@v4
+ - uses: ./actions/setup-java-with-retry
if: steps.filter.outputs.k8s-ingress == 'true'
with:
java-version: 17
distribution: "temurin"
+ cache: "maven"
+ cache-dependency-path: |
+ **/pom.xml
- name: Install Go
uses: actions/setup-go@v3
@@ -94,41 +88,17 @@ jobs:
curl -LO
https://storage.googleapis.com/kubernetes-release/release/v1.24.14/bin/linux/amd64/kubectl
&& sudo install kubectl /usr/local/bin/kubectl
kind create cluster --image=kindest/node:v1.21.1
--config=./shenyu-integrated-test/${{ matrix.case }}/deploy/kind-config.yaml
- - name: Cache Maven Repos
- if: steps.filter.outputs.k8s-ingress == 'true'
- uses: actions/cache@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
-
- - uses: actions/setup-java@v4
- if: steps.filter.outputs.k8s-ingress == 'true'
- with:
- java-version: 17
- distribution: "temurin"
-
- name: Install mvnd
if: steps.filter.outputs.k8s-ingress == 'true'
shell: bash
run: |
MVND_VERSION=1.0.2
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-windows-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-windows-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- else
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- fi
+ curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
+ unzip -q mvnd.zip
+ mkdir -p $HOME/.local
+ mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
+ echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
+ echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- name: Build with Maven
if: steps.filter.outputs.k8s-ingress == 'true'
@@ -136,14 +106,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build"
- mvnd -B clean install -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
+ mvnd -B -T 1C clean install -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
else
echo "Falling back to maven wrapper"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
- else
- ./mvnw -B clean install -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
- fi
+ ./mvnw -B -T 1C clean install -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
fi
- name: Build integrated tests
@@ -152,14 +118,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build integrated tests"
- mvnd -B clean install -Pit -DskipTests -am -f
./shenyu-integrated-test/pom.xml
+ mvnd -B -T 1C clean install -Pit -DskipTests -am -f
./shenyu-integrated-test/pom.xml
else
echo "Falling back to maven wrapper for integrated tests"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Pit -DskipTests -am -f
./shenyu-integrated-test/pom.xml
- else
- ./mvnw -B clean install -Pit -DskipTests -am -f
./shenyu-integrated-test/pom.xml
- fi
+ ./mvnw -B -T 1C clean install -Pit -DskipTests -am -f
./shenyu-integrated-test/pom.xml
fi
- name: Build examples
@@ -168,14 +130,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build examples"
- mvnd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
+ mvnd -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
else
echo "Falling back to maven wrapper for examples"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
- else
- ./mvnw -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
- fi
+ ./mvnw -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
fi
- name: Build k8s Cluster
@@ -183,8 +141,9 @@ jobs:
run: bash ./shenyu-integrated-test/${{ matrix.case
}}/script/build_k8s_cluster.sh
- name: Wait for k8s Cluster Start up
+ id: healthcheck
if: steps.filter.outputs.k8s-ingress == 'true'
- timeout-minutes: 15
+ timeout-minutes: 25
run: |
bash ./shenyu-integrated-test/${{ matrix.case
}}/script/healthcheck.sh
@@ -195,29 +154,30 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for running tests"
- mvnd test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
+ mvnd -T 1C test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
else
echo "Falling back to maven wrapper for tests"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
- else
- ./mvnw test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
- fi
+ ./mvnw -T 1C test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
fi
continue-on-error: true
- name: Cluster Test after Healthcheck
- if: steps.filter.outputs.k8s-ingress == 'true'
+ if: always() && steps.filter.outputs.k8s-ingress == 'true'
run: |
echo "----------kubectl get all -o wide----------"
- kubectl get all -o wide
+ kubectl get all -o wide || true
echo "----------kubectl get all -n shenyu-ingress -o wide----------"
- kubectl get all -n shenyu-ingress -o wide
+ kubectl get all -n shenyu-ingress -o wide || true
echo "----------kubectl get events --all-namespaces----------"
- kubectl get events --all-namespaces
+ kubectl get events --all-namespaces || true
+ echo "----------kubectl describe pod -n shenyu-ingress----------"
+ kubectl describe pod -n shenyu-ingress || true
echo "----------kubectl logs -l app=shenyu-ingress-controller -n
shenyu-ingress --tail=-1----------"
- kubectl logs -l app=shenyu-ingress-controller -n shenyu-ingress
--tail=-1
- if [[ ${{steps.test.outcome}} == "failure" ]]; then
+ kubectl logs -l app=shenyu-ingress-controller -n shenyu-ingress
--tail=-1 || true
+ if [[ ${{steps.healthcheck.outcome}} == "failure" ]]; then
+ echo "Healthcheck Failed"
+ exit 1
+ elif [[ ${{steps.test.outcome}} == "failure" ]]; then
echo "Test Failed"
exit 1
else
diff --git a/.github/workflows/integrated-test.yml
b/.github/workflows/integrated-test.yml
index 86073803a3..3bcce36ba3 100644
--- a/.github/workflows/integrated-test.yml
+++ b/.github/workflows/integrated-test.yml
@@ -74,53 +74,55 @@ jobs:
- '!NOTICE'
- '!.github/ISSUE_TEMPLATE/**'
- '!.github/PULL_REQUEST_TEMPLATE'
- - name: Restore ShenYu Maven Repos
- if: steps.filter.outputs.integration == 'true'
- uses: actions/cache/restore@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
- - uses: actions/setup-java@v4
+ - uses: ./actions/setup-java-with-retry
if: steps.filter.outputs.integration == 'true'
with:
java-version: 17
distribution: "temurin"
+ cache: "maven"
+ cache-dependency-path: |
+ **/pom.xml
- name: Install mvnd
if: steps.filter.outputs.integration == 'true'
shell: bash
run: |
MVND_VERSION=1.0.2
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-windows-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-windows-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- else
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- fi
+ curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
+ unzip -q mvnd.zip
+ mkdir -p $HOME/.local
+ mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
+ echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
+ echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
+ - name: Pre-pull docker base images
+ if: steps.filter.outputs.integration == 'true'
+ shell: bash
+ run: |
+ pull_with_retry() {
+ local image="$1"
+ for attempt in 1 2 3; do
+ if docker pull "$image"; then
+ return 0
+ fi
+ echo "docker pull $image failed on attempt $attempt"
+ sleep $((attempt * 10))
+ done
+ docker pull "$image"
+ }
+
+ pull_with_retry alpine:latest
+ pull_with_retry amazoncorretto:17.0.11-alpine3.19
+ pull_with_retry centos:7
+ pull_with_retry eclipse-temurin:17-centos7
- name: Build with Maven
if: steps.filter.outputs.integration == 'true'
shell: bash
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build"
- mvnd -B clean install -Prelease,docker -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
+ mvnd -B -T 1C clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
else
echo "Falling back to maven wrapper"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
- else
- ./mvnw -B clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
- fi
+ ./mvnw -B -T 1C clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
fi
- name: Build examples
if: steps.filter.outputs.integration == 'true'
@@ -128,14 +130,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build examples"
- mvnd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
+ mvnd -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
else
echo "Falling back to maven wrapper for examples"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
- else
- ./mvnw -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
- fi
+ ./mvnw -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -am -f ./shenyu-examples/pom.xml
fi
- name: Build integrated tests
if: steps.filter.outputs.integration == 'true'
@@ -143,14 +141,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build integrated tests"
- mvnd -B clean install -Pit -DskipTests -f
./shenyu-integrated-test/pom.xml
+ mvnd -B -T 1C clean install -Pit -DskipTests -f
./shenyu-integrated-test/pom.xml
else
echo "Falling back to maven wrapper for integrated tests"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Pit -DskipTests -f
./shenyu-integrated-test/pom.xml
- else
- ./mvnw -B clean install -Pit -DskipTests -f
./shenyu-integrated-test/pom.xml
- fi
+ ./mvnw -B -T 1C clean install -Pit -DskipTests -f
./shenyu-integrated-test/pom.xml
fi
- name: Start docker compose
if: steps.filter.outputs.integration == 'true'
@@ -168,14 +162,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for running tests"
- mvnd test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
+ mvnd -T 1C test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
else
echo "Falling back to maven wrapper for tests"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
- else
- ./mvnw test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
- fi
+ ./mvnw -T 1C test -Pit -f ./shenyu-integrated-test/${{ matrix.case
}}/pom.xml
fi
continue-on-error: true
- name: Check test result
diff --git a/.github/workflows/k8s-examples-http.yml
b/.github/workflows/k8s-examples-http.yml
index 7f3cb06de7..d8803f57bc 100644
--- a/.github/workflows/k8s-examples-http.yml
+++ b/.github/workflows/k8s-examples-http.yml
@@ -60,57 +60,55 @@ jobs:
- name: Install k8s
if: steps.filter.outputs.k8s-examples == 'true'
run: |
- curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.29.6+k3s2
K3S_KUBECONFIG_MODE=777 sh -
+ install_k3s() {
+ curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.29.6+k3s2
K3S_KUBECONFIG_MODE=777 sh -
+ }
+
+ for attempt in 1 2 3; do
+ if install_k3s; then
+ break
+ fi
+ if [ "$attempt" = 3 ]; then
+ echo "k3s install failed after $attempt attempts"
+ exit 1
+ fi
+ echo "k3s install failed on attempt $attempt"
+ sleep $((attempt * 15))
+ done
+
cat /etc/rancher/k3s/k3s.yaml
mkdir -p ~/.kube
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
- - name: Restore ShenYu Maven Repos
- if: steps.filter.outputs.k8s-examples == 'true'
- uses: actions/cache/restore@v3
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
- - uses: actions/setup-java@v4
+ - name: Setup Java and Maven cache
if: steps.filter.outputs.k8s-examples == 'true'
+ uses: ./actions/setup-java-with-retry
with:
java-version: 17
distribution: "temurin"
+ cache: "maven"
+ cache-dependency-path: |
+ **/pom.xml
- name: Install mvnd
if: steps.filter.outputs.k8s-examples == 'true'
shell: bash
run: |
MVND_VERSION=1.0.2
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-windows-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-windows-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- else
- curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
- unzip -q mvnd.zip
- mkdir -p $HOME/.local
- mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
- echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
- echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- fi
+ curl -sL
https://downloads.apache.org/maven/mvnd/${MVND_VERSION}/maven-mvnd-${MVND_VERSION}-linux-amd64.zip
-o mvnd.zip
+ unzip -q mvnd.zip
+ mkdir -p $HOME/.local
+ mv maven-mvnd-${MVND_VERSION}-linux-amd64 $HOME/.local/mvnd
+ echo "$HOME/.local/mvnd/bin" >> $GITHUB_PATH
+ echo "MVND_HOME=$HOME/.local/mvnd" >> $GITHUB_ENV
- name: Build with Maven
if: steps.filter.outputs.k8s-examples == 'true'
shell: bash
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build"
- mvnd -B clean install -Prelease,docker -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true
+ mvnd -B -T 1C clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
else
echo "Falling back to maven wrapper"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
- else
- ./mvnw -B clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
- fi
+ ./mvnw -B -T 1C clean install -Prelease,docker
-Dmaven.javadoc.skip=true -Dmaven.test.skip=true
fi
- name: Build examples
if: steps.filter.outputs.k8s-examples == 'true'
@@ -118,14 +116,10 @@ jobs:
run: |
if mvnd --version > /dev/null 2>&1; then
echo "Using mvnd for build examples"
- mvnd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -f ./shenyu-examples/pom.xml
+ mvnd -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -f ./shenyu-examples/pom.xml
else
echo "Falling back to maven wrapper for examples"
- if [[ "${{ runner.os }}" == "Windows" ]]; then
- ./mvnw.cmd -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -f ./shenyu-examples/pom.xml
- else
- ./mvnw -B clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -f ./shenyu-examples/pom.xml
- fi
+ ./mvnw -B -T 1C clean install -Pexample -Dmaven.javadoc.skip=true
-Dmaven.test.skip=true -f ./shenyu-examples/pom.xml
fi
- name: Build k8s Cluster
if: steps.filter.outputs.k8s-examples == 'true'
diff --git a/actions/setup-java-with-retry/action.yml
b/actions/setup-java-with-retry/action.yml
new file mode 100644
index 0000000000..68a4a2c24f
--- /dev/null
+++ b/actions/setup-java-with-retry/action.yml
@@ -0,0 +1,63 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+name: 'Setup Java With Retry'
+description: 'Retries actions/setup-java to reduce transient GitHub release
download failures.'
+inputs:
+ java-version:
+ description: 'Java version to install.'
+ required: true
+ distribution:
+ description: 'Java distribution to install.'
+ required: false
+ default: 'temurin'
+ cache:
+ description: 'Package manager cache to use.'
+ required: false
+ default: ''
+ cache-dependency-path:
+ description: 'Dependency file path for cache key generation.'
+ required: false
+ default: ''
+runs:
+ using: 'composite'
+ steps:
+ - name: Setup Java attempt 1
+ id: setup-java-1
+ uses: actions/setup-java@v4
+ continue-on-error: true
+ with:
+ java-version: ${{ inputs.java-version }}
+ distribution: ${{ inputs.distribution }}
+ cache: ${{ inputs.cache }}
+ cache-dependency-path: ${{ inputs.cache-dependency-path }}
+ - name: Setup Java attempt 2
+ id: setup-java-2
+ if: steps.setup-java-1.outcome == 'failure'
+ uses: actions/setup-java@v4
+ continue-on-error: true
+ with:
+ java-version: ${{ inputs.java-version }}
+ distribution: ${{ inputs.distribution }}
+ cache: ${{ inputs.cache }}
+ cache-dependency-path: ${{ inputs.cache-dependency-path }}
+ - name: Setup Java attempt 3
+ if: steps.setup-java-1.outcome == 'failure' &&
steps.setup-java-2.outcome == 'failure'
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ inputs.java-version }}
+ distribution: ${{ inputs.distribution }}
+ cache: ${{ inputs.cache }}
+ cache-dependency-path: ${{ inputs.cache-dependency-path }}
diff --git
a/shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service/k8s/shenyu-examples-dubbo.yml
b/shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service/k8s/shenyu-examples-dubbo.yml
index fb838b5091..5a056ab7f7 100644
---
a/shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service/k8s/shenyu-examples-dubbo.yml
+++
b/shenyu-examples/shenyu-examples-dubbo/shenyu-examples-apache-dubbo-service/k8s/shenyu-examples-dubbo.yml
@@ -48,7 +48,7 @@ spec:
command:
- /bin/sh
- "-c"
- - wget -q -O - http://localhost:8081/actuator/health | grep UP
|| exit 1
+ - wget -q -O - http://localhost:8011/actuator/health | grep UP
|| exit 1
env:
- name: shenyu.register.serverLists
value: http://shenyu-admin:9095
diff --git a/shenyu-examples/shenyu-examples-grpc/k8s/shenyu-examples-grpc.yml
b/shenyu-examples/shenyu-examples-grpc/k8s/shenyu-examples-grpc.yml
index 78733334ba..98c0c8e6dc 100644
--- a/shenyu-examples/shenyu-examples-grpc/k8s/shenyu-examples-grpc.yml
+++ b/shenyu-examples/shenyu-examples-grpc/k8s/shenyu-examples-grpc.yml
@@ -55,7 +55,7 @@ spec:
command:
- /bin/sh
- "-c"
- - wget -q -O - http://localhost:55290/actuator/health | grep
UP || exit 1
+ - wget -q -O - http://localhost:55290/grpc/actuator/health |
grep UP || exit 1
env:
- name: shenyu.register.serverLists
value: http://shenyu-admin:9095
diff --git
a/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
index 20e112e57f..8355132904 100644
---
a/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
+++
b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
@@ -363,6 +363,17 @@ public class HttpTestController {
return response;
}
+ /**
+ * Return accepted code.
+ *
+ * @return response. result bean
+ */
+ @PostMapping("/request/accepted")
+ @ResponseStatus(HttpStatus.ACCEPTED)
+ public ResultBean postAccepted() {
+ return accepted();
+ }
+
/**
* Return bad request code.
*
diff --git
a/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
index f0ab46a6a2..7282c44cea 100644
---
a/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
+++
b/shenyu-examples/shenyu-examples-sofa/shenyu-examples-sofa-service/k8s/shenyu-examples-sofa.yml
@@ -48,7 +48,7 @@ spec:
command:
- /bin/sh
- "-c"
- - wget -q -O - http://localhost:8081/actuator/health | grep UP
|| exit 1
+ - wget -q -O - http://localhost:28011/actuator/health | grep
UP || exit 1
env:
- name: shenyu.register.serverLists
value: http://shenyu-admin:9095
diff --git
a/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/k8s/shenyu-example-spring-annotation-websocket.yml
b/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/k8s/shenyu-example-spring-annotation-websocket.yml
index 0767297926..0ddbac62e5 100644
---
a/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/k8s/shenyu-example-spring-annotation-websocket.yml
+++
b/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/k8s/shenyu-example-spring-annotation-websocket.yml
@@ -37,8 +37,17 @@ spec:
containers:
- image: shenyu-example-spring-annotation-websocket
name: shenyu-example-spring-annotation-websocket
+ startupProbe:
+ periodSeconds: 10
+ timeoutSeconds: 5
+ failureThreshold: 18
+ exec:
+ command:
+ - /bin/sh
+ - "-c"
+ - wget -q -O - http://localhost:8001/actuator/health | grep UP
|| exit 1
livenessProbe:
- initialDelaySeconds: 30
+ initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
diff --git
a/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/pom.xml
b/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/pom.xml
index c3af4bbe53..fe92b0f8d9 100644
---
a/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/pom.xml
+++
b/shenyu-examples/shenyu-examples-websocket/shenyu-example-spring-annotation-websocket/pom.xml
@@ -41,6 +41,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-actuator</artifactId>
+ </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
diff --git a/shenyu-integrated-test/k8s-ingress-healthcheck.sh
b/shenyu-integrated-test/k8s-ingress-healthcheck.sh
new file mode 100644
index 0000000000..ca3030eb31
--- /dev/null
+++ b/shenyu-integrated-test/k8s-ingress-healthcheck.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+set -euo pipefail
+
+SERVICES_FILE=$1
+STARTUP_DELAY_SECONDS=${2:-3}
+MAX_RETRIES=${MAX_RETRIES:-30}
+CURL_CONNECT_TIMEOUT=${CURL_CONNECT_TIMEOUT:-5}
+CURL_MAX_TIME=${CURL_MAX_TIME:-10}
+K8S_WAIT_TIMEOUT=${K8S_WAIT_TIMEOUT:-10m}
+
+wait_deployments() {
+ local namespace=$1
+ local required=${2:-true}
+
+ echo "waiting for ${namespace} deployments to become available, timeout
${K8S_WAIT_TIMEOUT}"
+ deployments=$(kubectl get deployment -n "${namespace}" -o name)
+ if [ -z "${deployments}" ]; then
+ echo "no deployments found in namespace ${namespace}"
+ if [ "${required}" != "true" ]; then
+ return 0
+ fi
+ exit 1
+ fi
+
+ for deployment in ${deployments}; do
+ kubectl wait --for=condition=Available "${deployment}" -n
"${namespace}" --timeout="${K8S_WAIT_TIMEOUT}"
+ done
+}
+
+wait_deployments shenyu-ingress
+wait_deployments default false
+
+failed=0
+while IFS= read -r service || [ -n "$service" ]; do
+ if [[ -z "$service" || "$service" =~ ^# ]]; then
+ continue
+ fi
+
+ ready=0
+ for loop in $(seq 1 "${MAX_RETRIES}"); do
+ status=$(curl --connect-timeout "${CURL_CONNECT_TIMEOUT}" --max-time
"${CURL_MAX_TIME}" -o /dev/null -s -w "%{http_code}" "$service" || true)
+ echo -e "curl $service response $status, attempt $loop/${MAX_RETRIES}"
+
+ if [ "$status" = "200" ]; then
+ ready=1
+ break
+ fi
+
+ sleep 2
+ done
+
+ if [ "$ready" -ne 1 ]; then
+ echo "service $service failed healthcheck after ${MAX_RETRIES}
attempts"
+ failed=1
+ fi
+done < "${SERVICES_FILE}"
+
+sleep "${STARTUP_DELAY_SECONDS}"
+echo -e "\n-------------------"
+exit "${failed}"
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
index ffe81bf29f..210b3a2bc0 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
+++
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/SentinelPluginTest.java
@@ -27,7 +27,7 @@ import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.JsonUtils;
import org.apache.shenyu.integratedtest.common.AbstractPluginDataInit;
import org.apache.shenyu.integratedtest.common.helper.HttpHelper;
-import org.apache.shenyu.integratedtest.common.result.ResultBean;
+import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
import org.apache.shenyu.web.controller.LocalPluginController.RuleLocalData;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -48,7 +48,7 @@ public final class SentinelPluginTest extends
AbstractPluginDataInit {
private static final String TEST_SENTINEL_PATH =
"/http/test/sentinel/pass";
- private static final String TEST_SENTINEL_FALLBACK_PATH =
"/http/test/request/accepted";
+ private static final String TEST_SENTINEL_FALLBACK_PATH =
"fallback:/fallback/sentinel";
@BeforeEach
public void setup() throws IOException {
@@ -82,8 +82,9 @@ public final class SentinelPluginTest extends
AbstractPluginDataInit {
Map<String, Object> result =
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType);
assertNotNull(result);
assertEquals("pass", result.get("msg"));
- ResultBean fallbackRet =
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, ResultBean.class);
- assertEquals(202, fallbackRet.getCode());
+ Map<String, Object> fallbackRet =
HttpHelper.INSTANCE.postGateway(TEST_SENTINEL_PATH, returnType);
+ assertEquals(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getCode(),
((Number) fallbackRet.get("code")).intValue());
+ assertEquals(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK.getMsg(),
fallbackRet.get("message"));
}
private static List<ConditionData> buildSelectorConditionList() {
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/deploy/kind-config.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/deploy/kind-config.yaml
index 0dc0313af1..7cbb7298ac 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/deploy/kind-config.yaml
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/deploy/kind-config.yaml
@@ -34,3 +34,6 @@ nodes:
- containerPort: 30095
hostPort: 30095
protocol: TCP
+ - containerPort: 31187
+ hostPort: 31187
+ protocol: TCP
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/script/healthcheck.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/script/healthcheck.sh
index 22a76034ab..63a920d983 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/script/healthcheck.sh
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-apache-dubbo/script/healthcheck.sh
@@ -17,20 +17,4 @@
#
PRGDIR=`dirname "$0"`
-for service in `grep -v -E "^$|^#" ${PRGDIR}/services.list`
-do
- for loop in `seq 1 30`
- do
- status=`curl -o /dev/null -s -w %{http_code} $service`
- echo -e "curl $service response $status"
-
- if [ $status -eq 200 ]; then
- break
- fi
-
- sleep 2
- done
-done
-
-sleep 3
-echo -e "\n-------------------"
+bash "${PRGDIR}/../../k8s-ingress-healthcheck.sh" "${PRGDIR}/services.list" 3
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/deploy/kind-config.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/deploy/kind-config.yaml
index 0dc0313af1..ee65b29b5f 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/deploy/kind-config.yaml
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/deploy/kind-config.yaml
@@ -34,3 +34,6 @@ nodes:
- containerPort: 30095
hostPort: 30095
protocol: TCP
+ - containerPort: 31183
+ hostPort: 31183
+ protocol: TCP
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/healthcheck.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/healthcheck.sh
index 22a76034ab..63a920d983 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/healthcheck.sh
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/healthcheck.sh
@@ -17,20 +17,4 @@
#
PRGDIR=`dirname "$0"`
-for service in `grep -v -E "^$|^#" ${PRGDIR}/services.list`
-do
- for loop in `seq 1 30`
- do
- status=`curl -o /dev/null -s -w %{http_code} $service`
- echo -e "curl $service response $status"
-
- if [ $status -eq 200 ]; then
- break
- fi
-
- sleep 2
- done
-done
-
-sleep 3
-echo -e "\n-------------------"
+bash "${PRGDIR}/../../k8s-ingress-healthcheck.sh" "${PRGDIR}/services.list" 3
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/services.list
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/services.list
index ef0e742c04..722dc8fce2 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/services.list
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-grpc/script/services.list
@@ -15,4 +15,4 @@
# limitations under the License.
http://localhost:30095/actuator/health
-http://localhost:31184/actuator/health
+http://localhost:31183/grpc/actuator/health
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/deploy/kind-config.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/deploy/kind-config.yaml
index 0dc0313af1..05fc45360e 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/deploy/kind-config.yaml
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/deploy/kind-config.yaml
@@ -34,3 +34,6 @@ nodes:
- containerPort: 30095
hostPort: 30095
protocol: TCP
+ - containerPort: 31189
+ hostPort: 31189
+ protocol: TCP
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/script/healthcheck.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/script/healthcheck.sh
index 896e784144..a2e645436b 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/script/healthcheck.sh
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-http/script/healthcheck.sh
@@ -17,20 +17,4 @@
#
PRGDIR=`dirname "$0"`
-for service in `grep -v -E "^$|^#" ${PRGDIR}/services.list`
-do
- for loop in `seq 1 30`
- do
- status=`curl -o /dev/null -s -w %{http_code} $service`
- echo -e "curl $service response $status"
-
- if [ $status -eq 200 ]; then
- break
- fi
-
- sleep 2
- done
-done
-
-sleep 20
-echo -e "\n-------------------"
+bash "${PRGDIR}/../../k8s-ingress-healthcheck.sh" "${PRGDIR}/services.list" 20
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
index 0dc0313af1..bdd2b447ea 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/deploy/kind-config.yaml
@@ -34,3 +34,6 @@ nodes:
- containerPort: 30095
hostPort: 30095
protocol: TCP
+ - containerPort: 31112
+ hostPort: 31112
+ protocol: TCP
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
index ef0e742c04..b539a8f5ac 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-sofa/script/services.list
@@ -15,4 +15,4 @@
# limitations under the License.
http://localhost:30095/actuator/health
-http://localhost:31184/actuator/health
+http://localhost:31112/actuator/health
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/healthcheck.sh
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/healthcheck.sh
index 896e784144..a2e645436b 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/healthcheck.sh
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/healthcheck.sh
@@ -17,20 +17,4 @@
#
PRGDIR=`dirname "$0"`
-for service in `grep -v -E "^$|^#" ${PRGDIR}/services.list`
-do
- for loop in `seq 1 30`
- do
- status=`curl -o /dev/null -s -w %{http_code} $service`
- echo -e "curl $service response $status"
-
- if [ $status -eq 200 ]; then
- break
- fi
-
- sleep 2
- done
-done
-
-sleep 20
-echo -e "\n-------------------"
+bash "${PRGDIR}/../../k8s-ingress-healthcheck.sh" "${PRGDIR}/services.list" 20
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/services.list
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/services.list
index d6f04cdb1e..2852c6e404 100644
---
a/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/services.list
+++
b/shenyu-integrated-test/shenyu-integrated-test-k8s-ingress-websocket/script/services.list
@@ -14,4 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-http://localhost:31188/new/feature/get/mapping/path
+http://localhost:30095/actuator/health
diff --git
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/EndpointsReconciler.java
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/EndpointsReconciler.java
index b730336314..b02ccac54d 100644
---
a/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/EndpointsReconciler.java
+++
b/shenyu-kubernetes-controller/src/main/java/org/apache/shenyu/k8s/reconciler/EndpointsReconciler.java
@@ -32,6 +32,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.dto.convert.selector.DivideUpstream;
+import org.apache.shenyu.common.dto.convert.selector.WebSocketUpstream;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.exception.ShenyuException;
import org.apache.shenyu.common.utils.GsonUtils;
@@ -42,6 +43,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -54,6 +56,8 @@ public class EndpointsReconciler implements Reconciler {
private static final Logger LOG =
LoggerFactory.getLogger(EndpointsReconciler.class);
+ private static final Set<String> ENDPOINT_UPSTREAM_PLUGINS = new
HashSet<>(Arrays.asList(PluginEnum.DIVIDE.getName(),
PluginEnum.WEB_SOCKET.getName()));
+
private final Lister<V1Ingress> ingressLister;
private final Lister<V1Endpoints> endpointsLister;
@@ -101,17 +105,31 @@ public class EndpointsReconciler implements Reconciler {
return new Result(false);
}
- // 1. Obtain upstream according to endpoints
- List<DivideUpstream> upstreamList =
getUpstreamFromEndpoints(v1Endpoints);
+ updateSelectors(ingressList, PluginEnum.DIVIDE.getName(),
getDivideUpstreamFromEndpoints(v1Endpoints));
+ updateSelectors(ingressList, PluginEnum.WEB_SOCKET.getName(),
getWebSocketUpstreamFromEndpoints(v1Endpoints));
+ LOG.info("Update selector for endpoint {}", request);
+
+ return new Result(false);
+ }
- // 2. Update the handler of the selector
- List<SelectorData> totalSelectors =
shenyuCacheRepository.findSelectorDataList(PluginEnum.DIVIDE.getName());
+ private void updateSelectors(final List<Pair<String, String>> ingressList,
final String pluginName, final String handle) {
+ if (!ENDPOINT_UPSTREAM_PLUGINS.contains(pluginName)) {
+ return;
+ }
+ List<SelectorData> totalSelectors =
shenyuCacheRepository.findSelectorDataList(pluginName);
+ if (CollectionUtils.isEmpty(totalSelectors)) {
+ return;
+ }
Set<String> needUpdateSelectorId = new HashSet<>();
- //TODO Adaptation of other plugins
ingressList.forEach(item -> {
- List<String> selectorIdList =
IngressSelectorCache.getInstance().get(item.getLeft(), item.getRight(),
PluginEnum.DIVIDE.getName());
- needUpdateSelectorId.addAll(selectorIdList);
+ List<String> selectorIdList =
IngressSelectorCache.getInstance().get(item.getLeft(), item.getRight(),
pluginName);
+ if (CollectionUtils.isNotEmpty(selectorIdList)) {
+ needUpdateSelectorId.addAll(selectorIdList);
+ }
});
+ if (needUpdateSelectorId.isEmpty()) {
+ return;
+ }
totalSelectors.forEach(selectorData -> {
if (needUpdateSelectorId.contains(selectorData.getId())) {
SelectorData newSelectorData =
SelectorData.builder().id(selectorData.getId())
@@ -124,19 +142,45 @@ public class EndpointsReconciler implements Reconciler {
.enabled(selectorData.getEnabled())
.logged(selectorData.getLogged())
.continued(selectorData.getContinued())
- .handle(GsonUtils.getInstance().toJson(upstreamList))
+ .handle(handle)
.conditionList(selectorData.getConditionList())
.matchRestful(selectorData.getMatchRestful()).build();
shenyuCacheRepository.saveOrUpdateSelectorData(newSelectorData);
}
});
- LOG.info("Update selector for endpoint {}", request);
-
- return new Result(false);
}
- private List<DivideUpstream> getUpstreamFromEndpoints(final V1Endpoints
v1Endpoints) {
+ private String getDivideUpstreamFromEndpoints(final V1Endpoints
v1Endpoints) {
List<DivideUpstream> res = new ArrayList<>();
+ endpointAddresses(v1Endpoints).forEach(pair -> {
+ DivideUpstream upstream = new DivideUpstream();
+ upstream.setUpstreamUrl(pair.getLeft().getIp() + ":" +
pair.getRight());
+ upstream.setWeight(100);
+ // TODO support config protocol in annotation
+ upstream.setProtocol("http://");
+ upstream.setWarmup(0);
+ upstream.setStatus(true);
+ upstream.setUpstreamHost("");
+ res.add(upstream);
+ });
+ return GsonUtils.getInstance().toJson(res);
+ }
+
+ private String getWebSocketUpstreamFromEndpoints(final V1Endpoints
v1Endpoints) {
+ List<WebSocketUpstream> res = new ArrayList<>();
+ endpointAddresses(v1Endpoints).forEach(pair ->
res.add(WebSocketUpstream.builder()
+ .upstreamUrl(pair.getLeft().getIp() + ":" + pair.getRight())
+ .weight(100)
+ .protocol("ws://")
+ .warmup(0)
+ .status(true)
+ .host("")
+ .build()));
+ return GsonUtils.getInstance().toJson(res);
+ }
+
+ private List<Pair<V1EndpointAddress, String>> endpointAddresses(final
V1Endpoints v1Endpoints) {
+ List<Pair<V1EndpointAddress, String>> res = new ArrayList<>();
List<V1EndpointSubset> subsets = v1Endpoints.getSubsets();
if (CollectionUtils.isNotEmpty(subsets)) {
for (V1EndpointSubset subset : subsets) {
@@ -159,17 +203,8 @@ public class EndpointsReconciler implements Reconciler {
}
}
for (V1EndpointAddress address : addresses) {
- String ip = address.getIp();
- if (Objects.nonNull(ip)) {
- DivideUpstream upstream = new DivideUpstream();
- upstream.setUpstreamUrl(ip + ":" + port);
- upstream.setWeight(100);
- // TODO support config protocol in annotation
- upstream.setProtocol("http://");
- upstream.setWarmup(0);
- upstream.setStatus(true);
- upstream.setUpstreamHost("");
- res.add(upstream);
+ if (Objects.nonNull(address.getIp()) &&
Objects.nonNull(port)) {
+ res.add(Pair.of(address, port));
}
}
}
diff --git
a/shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/EndpointsReconcilerTest.java
b/shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/EndpointsReconcilerTest.java
new file mode 100644
index 0000000000..5d36eea3a9
--- /dev/null
+++
b/shenyu-kubernetes-controller/src/test/java/org/apache/shenyu/k8s/EndpointsReconcilerTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.shenyu.k8s;
+
+import io.kubernetes.client.extended.controller.reconciler.Request;
+import io.kubernetes.client.extended.controller.reconciler.Result;
+import io.kubernetes.client.informer.SharedIndexInformer;
+import io.kubernetes.client.informer.cache.Indexer;
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
+import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1EndpointSubsetBuilder;
+import io.kubernetes.client.openapi.models.V1Endpoints;
+import io.kubernetes.client.openapi.models.V1EndpointsBuilder;
+import io.kubernetes.client.openapi.models.V1Ingress;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.k8s.cache.IngressSelectorCache;
+import org.apache.shenyu.k8s.cache.ServiceIngressCache;
+import org.apache.shenyu.k8s.reconciler.EndpointsReconciler;
+import org.apache.shenyu.k8s.repository.ShenyuCacheRepository;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Endpoints reconciler test.
+ */
+public final class EndpointsReconcilerTest {
+
+ /**
+ * test websocket selector update.
+ */
+ @Test
+ public void testUpdateWebSocketSelector() {
+ SharedIndexInformer<V1Ingress> ingressInformer =
mock(SharedIndexInformer.class);
+ SharedIndexInformer<V1Endpoints> endpointsInformer =
mock(SharedIndexInformer.class);
+ Indexer<V1Ingress> ingressIndexer = mock(Indexer.class);
+ Indexer<V1Endpoints> endpointsIndexer = mock(Indexer.class);
+ when(ingressInformer.getIndexer()).thenReturn(ingressIndexer);
+ when(endpointsInformer.getIndexer()).thenReturn(endpointsIndexer);
+
+ String namespace = "endpoint-websocket-ns";
+ String serviceName = "endpoint-websocket-service";
+ String ingressName = "endpoint-websocket-ingress";
+ String selectorId = "endpoint-websocket-selector";
+ V1Endpoints endpoints = new V1EndpointsBuilder().withKind("Endpoints")
+
.withNewMetadata().withNamespace(namespace).withName(serviceName).endMetadata()
+ .withSubsets(new V1EndpointSubsetBuilder()
+ .withAddresses(new V1EndpointAddress().ip("127.0.0.1"))
+ .withPorts(new
CoreV1EndpointPort().port(8001).protocol("TCP"))
+ .build())
+ .build();
+ when(endpointsIndexer.getByKey(namespace + "/" +
serviceName)).thenReturn(endpoints);
+
+ ServiceIngressCache.getInstance().putIngressName(namespace,
serviceName, namespace, ingressName);
+ IngressSelectorCache.getInstance().put(namespace, ingressName,
PluginEnum.WEB_SOCKET.getName(), selectorId);
+
+ ShenyuCacheRepository shenyuCacheRepository =
mock(ShenyuCacheRepository.class);
+ SelectorData selectorData = SelectorData.builder()
+ .id(selectorId)
+ .pluginId(String.valueOf(PluginEnum.WEB_SOCKET.getCode()))
+ .pluginName(PluginEnum.WEB_SOCKET.getName())
+ .name("/**")
+ .handle("[]")
+ .enabled(true)
+ .build();
+
when(shenyuCacheRepository.findSelectorDataList(PluginEnum.DIVIDE.getName())).thenReturn(Collections.emptyList());
+
when(shenyuCacheRepository.findSelectorDataList(PluginEnum.WEB_SOCKET.getName())).thenReturn(Collections.singletonList(selectorData));
+
+ EndpointsReconciler endpointsReconciler = new
EndpointsReconciler(ingressInformer, endpointsInformer, shenyuCacheRepository,
mock(ApiClient.class));
+ Result result = endpointsReconciler.reconcile(new Request(namespace,
serviceName));
+
+ Assertions.assertEquals(new Result(false), result);
+ ArgumentCaptor<SelectorData> selectorCaptor =
ArgumentCaptor.forClass(SelectorData.class);
+
verify(shenyuCacheRepository).saveOrUpdateSelectorData(selectorCaptor.capture());
+ SelectorData updatedSelector = selectorCaptor.getValue();
+ Assertions.assertEquals(PluginEnum.WEB_SOCKET.getName(),
updatedSelector.getPluginName());
+ assertThat(updatedSelector.getHandle(),
containsString("\"protocol\":\"ws://\""));
+ assertThat(updatedSelector.getHandle(),
containsString("\"upstreamUrl\":\"127.0.0.1:8001\""));
+ }
+}
diff --git
a/shenyu-web/src/main/java/org/apache/shenyu/web/fallback/DefaultFallbackController.java
b/shenyu-web/src/main/java/org/apache/shenyu/web/fallback/DefaultFallbackController.java
index 4b375f8158..b4a14e4dc1 100644
---
a/shenyu-web/src/main/java/org/apache/shenyu/web/fallback/DefaultFallbackController.java
+++
b/shenyu-web/src/main/java/org/apache/shenyu/web/fallback/DefaultFallbackController.java
@@ -19,12 +19,11 @@ package org.apache.shenyu.web.fallback;
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
import org.apache.shenyu.plugin.api.result.ShenyuResultWrap;
-import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
- * The default fallback for hystrix plugin and resilience4j plugin.
+ * The default fallback for fault tolerance plugins.
*/
@RestController
@RequestMapping("/fallback")
@@ -35,7 +34,7 @@ public class DefaultFallbackController {
*
* @return the shenyu result
*/
- @GetMapping("/hystrix")
+ @RequestMapping("/hystrix")
public Object hystrixPluginFallback() {
return
ShenyuResultWrap.error(ShenyuResultEnum.HYSTRIX_PLUGIN_FALLBACK, null);
}
@@ -45,7 +44,7 @@ public class DefaultFallbackController {
*
* @return the shenyu result
*/
- @GetMapping("/resilience4j")
+ @RequestMapping("/resilience4j")
public Object resilience4jFallBack() {
return
ShenyuResultWrap.error(ShenyuResultEnum.RESILIENCE4J_PLUGIN_FALLBACK, null);
}
@@ -55,7 +54,7 @@ public class DefaultFallbackController {
*
* @return the object
*/
- @GetMapping("/sentinel")
+ @RequestMapping("/sentinel")
public Object sentinelFallBack() {
return
ShenyuResultWrap.error(ShenyuResultEnum.SENTINEL_PLUGIN_FALLBACK, null);
}
diff --git
a/shenyu-web/src/test/java/org/apache/shenyu/web/fallback/DefaultFallbackControllerTest.java
b/shenyu-web/src/test/java/org/apache/shenyu/web/fallback/DefaultFallbackControllerTest.java
index aa5e608736..608b496a66 100644
---
a/shenyu-web/src/test/java/org/apache/shenyu/web/fallback/DefaultFallbackControllerTest.java
+++
b/shenyu-web/src/test/java/org/apache/shenyu/web/fallback/DefaultFallbackControllerTest.java
@@ -66,4 +66,11 @@ public final class DefaultFallbackControllerTest {
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
+ @Test
+ public void sentinelFallbackSupportsOriginalRequestMethod() throws
Exception {
+ final MockHttpServletResponse response =
this.mockMvc.perform(MockMvcRequestBuilders.post("/fallback/sentinel"))
+ .andReturn().getResponse();
+ assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
+ }
+
}