This is an automated email from the ASF dual-hosted git repository.
xiaoyu 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 f056da8f7 [type:e2e] setup github action for e2e and fix some issues
(#3971)
f056da8f7 is described below
commit f056da8f7bc5ab394ac77343e26b4e87ffc3943d
Author: Luke.Z <[email protected]>
AuthorDate: Wed Sep 21 20:46:08 2022 +0800
[type:e2e] setup github action for e2e and fix some issues (#3971)
* fix test and setup GitHub action
* update images of docker-compose
---
.github/workflows/e2e.yaml | 121 +++++++++++++++++++++
shenyu-e2e/pom.xml | 30 +++++
.../e2e/testcase/common/function/HttpCheckers.java | 4 +-
.../testcase/common/function/WaitForHelper.java | 7 +-
.../common/function/WaitForHelperTest.java | 12 +-
.../e2e/testcase/plugin/DividePluginCases.java | 5 +-
.../shenyu/e2e/testcase/plugin/PluginsTest.java | 12 +-
.../src/test/resources/docker-compose.base.yml | 36 ++++++
.../src/test/resources/docker-compose.yml | 36 ++++++
.../shenyu/e2e/client/admin/AdminClient.java | 10 +-
.../shenyu/e2e/client/admin/model/Plugin.java | 35 ++++++
.../apache/shenyu/e2e/matcher/SelectorMatcher.java | 1 -
.../shenyu/e2e/client/admin/AdminClientTest.java | 2 +-
.../src/test/resources/docker-compose.base.yaml | 16 ---
.../src/test/resources/docker-compose.yml | 24 ----
.../e2e/engine/service/DockerServiceCompose.java | 86 ++++++++-------
.../e2e/engine/service/HostServiceCompose.java | 13 ++-
.../shenyu/e2e/engine/service/NamingResolver.java | 111 +++++++++++++++++++
.../e2e/engine/service/WaitingForStrategies.java | 49 +++++++++
.../engine/service/docker/ShenYuLogConsumer.java | 3 +-
.../apache/shenyu/e2e/engine/ShenYuEngineTest.java | 32 +++++-
.../src/test/resources/docker-compose.yml | 24 ++++
22 files changed, 555 insertions(+), 114 deletions(-)
diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
new file mode 100644
index 000000000..09a5b32b4
--- /dev/null
+++ b/.github/workflows/e2e.yaml
@@ -0,0 +1,121 @@
+# 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: e2e
+
+on:
+ pull_request:
+ branches:
+ - master
+ paths-ignore:
+ - '**.md'
+ - '**/resources/static/'
+ push:
+ branches:
+ - master
+ paths-ignore:
+ - '**.md'
+ - '**/resources/static/'
+
+env:
+ TAG: ${{ github.sha }}
+
+jobs:
+ build-docker-images:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Set up JDK 11 for Compiling ShenYu E2E Egnine
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Build shenyu-e2e-engin with Maven
+ run: ./mvnw -B -f ./shenyu-e2e/pom.xml -am -pl shenyu-e2e-engine clean
install
+ - name: Set up JDK 8 for Building ShenYu
+ uses: actions/setup-java@v3
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Build Apache ShenYu with Maven
+ run: ./mvnw -B clean -Prelease -Dmaven.javadoc.skip=true -B
-Drat.skip=true -Djacoco.skip=true -DskipITs -DskipTests package
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Build and Export Admin Image
+ uses: docker/build-push-action@v3
+ with:
+ context: ./shenyu-dist/shenyu-admin-dist
+ build-args: APP_NAME=apache-shenyu-*-admin-bin
+ tags: shenyu/admin:latest
+ outputs: type=docker,dest=/tmp/apache-shenyu-admin.tar
+
+ - name: Build and Export Bootstrap Image
+ uses: docker/build-push-action@v3
+ with:
+ context: ./shenyu-dist/shenyu-bootstrap-dist
+ build-args: APP_NAME=apache-shenyu-*-bootstrap-bin
+ tags: shenyu/bootstrap:latest
+ outputs: type=docker,dest=/tmp/apache-shenyu-bootstrap.tar
+
+ - name: Upload Docker Image Artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: shenyu-images
+ path: /tmp/apache-shenyu-*.tar
+ retention-days: 1
+
+ e2e:
+ runs-on: ubuntu-latest
+ needs: [ "build-docker-images" ]
+ strategy:
+ matrix:
+ storage: [ "h2" ]
+ steps:
+ - uses: actions/download-artifact@v3
+ with:
+ name: shenyu-images
+ path: /tmp/
+
+ - name: Load ShenYu Docker Images
+ run: |
+ docker load --input /tmp/apache-shenyu-admin.tar
+ docker load --input /tmp/apache-shenyu-bootstrap.tar
+ docker image ls -a
+
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: maven
+
+ - name: Run ShenYu E2E Tests
+ env:
+ storage: ${{ matrix.storage }}
+ run: |
+ ./mvnw -B -f shenyu-e2e/pom.xml -pl shenyu-e2e-case test
diff --git a/shenyu-e2e/pom.xml b/shenyu-e2e/pom.xml
index dced74963..ec2770264 100644
--- a/shenyu-e2e/pom.xml
+++ b/shenyu-e2e/pom.xml
@@ -23,6 +23,7 @@
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>21</version>
+ <relativePath>./pom.xml</relativePath>
</parent>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-e2e</artifactId>
@@ -214,6 +215,14 @@
<artifactId>json-path</artifactId>
<version>${rest-assured.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>21</version>
+ <scope>import</scope>
+ <type>pom</type>
+ </dependency>
</dependencies>
</dependencyManagement>
@@ -226,6 +235,27 @@
<target>${java.version}</target>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>3.0.0-M7</version>
+ <dependencies>
+ <dependency>
+ <groupId>me.fabriciorby</groupId>
+
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
+ <version>0.1.0</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <reportFormat>plain</reportFormat>
+ <consoleOutputReporter>
+ <disable>true</disable>
+ </consoleOutputReporter>
+ <statelessTestsetInfoReporter
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter"/>
+ </configuration>
+ </plugin>
+
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/HttpCheckers.java
b/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/HttpCheckers.java
index 4bf882b3f..136f1e0f6 100644
---
a/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/HttpCheckers.java
+++
b/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/HttpCheckers.java
@@ -37,7 +37,7 @@ public class HttpCheckers {
.log()
.ifValidationFails()
.body("code", lessThan(0))
- .body("message", containsString("Can not find selector,
please check your configuration!"));
+ .body("message", containsString("please check your
configuration!"));
};
}
@@ -52,7 +52,7 @@ public class HttpCheckers {
.log()
.ifValidationFails()
.body("code", nullValue())
- .body("message", not(containsString("Can not find
selector, please check your configuration!")));
+ .body("message", not(containsString("please check your
configuration!")));
};
}
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelper.java
b/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelper.java
index ae99cc4aa..de073838f 100644
---
a/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelper.java
+++
b/shenyu-e2e/shenyu-e2e-case/src/main/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelper.java
@@ -65,20 +65,19 @@ public class WaitForHelper {
}
return;
} catch (AssertionError e) {
- log.info("failed to check endpoint '" + endpoint + "'\n
{}", e.getMessage());
+ log.debug("failed to check endpoint '" + endpoint + "'\n
{}", e.getMessage());
}
try {
TimeUnit.MILLISECONDS.sleep(timeInRetry.toMillis());
} catch (InterruptedException ignore) {
- break;
}
}
- log.info("check endpoint({}) successful", endpoint);
});
try {
future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
+ log.info("check endpoint({}) successful", endpoint);
} catch (ExecutionException | InterruptedException | TimeoutException
e) {
future.cancel(true);
throw new TimeoutException("checking endpoint '" + endpoint + "'
timeout after " + timeout);
@@ -93,7 +92,7 @@ public class WaitForHelper {
checker.check(supplier);
return;
} catch (AssertionError e) {
- log.info(e.getMessage());
+ log.debug(e.getMessage());
}
try {
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelperTest.java
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelperTest.java
index 2f6e70f93..afb60e382 100644
---
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelperTest.java
+++
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/common/function/WaitForHelperTest.java
@@ -32,12 +32,12 @@ import static org.hamcrest.CoreMatchers.containsString;
public class WaitForHelperTest {
@Test
- void test() throws TimeoutException {
+ void testSuccess() throws TimeoutException {
new WaitForHelper().waitFor(
- () -> given().baseUri("http://localhost:8080").when(),
+ () -> given().baseUri("http://httpbin.org").when(),
Method.GET,
- "/delay/1",
- new ResponseSpecBuilder().expectBody("url",
containsString("/anything")).build()
+ "/delay/0",
+ new ResponseSpecBuilder().expectBody("url",
containsString("/delay/0")).build()
);
}
@@ -45,10 +45,10 @@ public class WaitForHelperTest {
void testTimeout() {
Assertions.assertThrows(TimeoutException.class, () -> {
new WaitForHelper(10, Duration.ofSeconds(1),
Duration.ofMillis(500)).waitFor(
- () -> given().baseUri("http://localhost:8080").when(),
+ () -> given().baseUri("http://httpbin.org").when(),
Method.GET,
"/delay/1x",
- new ResponseSpecBuilder().expectBody("message",
containsString("/anything")).build()
+ new ResponseSpecBuilder().expectBody("message",
containsString("/delay/1x")).build()
);
});
}
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/DividePluginCases.java
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/DividePluginCases.java
index cd61b2e7b..a9732e4ac 100644
---
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/DividePluginCases.java
+++
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/DividePluginCases.java
@@ -22,6 +22,7 @@ import
org.apache.shenyu.e2e.client.admin.model.data.Condition.Operator;
import org.apache.shenyu.e2e.client.admin.model.data.Condition.ParamType;
import org.apache.shenyu.e2e.engine.scenario.ShenYuScenarioProvider;
import org.apache.shenyu.e2e.engine.scenario.specification.ScenarioSpec;
+import org.apache.shenyu.e2e.engine.service.NamingResolver;
import org.apache.shenyu.e2e.testcase.common.specification.ShenYuAfterEachSpec;
import
org.apache.shenyu.e2e.testcase.common.specification.ShenYuBeforeEachSpec;
import org.apache.shenyu.e2e.testcase.common.specification.ShenYuCaseSpec;
@@ -42,7 +43,7 @@ public class DividePluginCases implements
ShenYuScenarioProvider {
@Override
public List<ScenarioSpec> get() {
-
+ final String address = NamingResolver.INSTANCE.resolve("httpbin");
return List.of(
ShenYuScenarioSpec.builder()
.name("divide-single rule[uri=]")
@@ -50,7 +51,7 @@ public class DividePluginCases implements
ShenYuScenarioProvider {
ShenYuBeforeEachSpec.builder()
.addSelectorAndRule(
newSelectorBuilder("httpbin",
Plugin.DIVIDE)
-
.handle(newUpstreamsBuilder("34.227.213.82:80"))
+
.handle(newUpstreamsBuilder(address + ":80"))
.conditionList(newConditions(ParamType.URI, Operator.EQUAL, ANYTHING))
.build(),
newRuleBuilder("rule")
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/PluginsTest.java
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/PluginsTest.java
index 053e76016..a7ef98804 100644
---
a/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/PluginsTest.java
+++
b/shenyu-e2e/shenyu-e2e-case/src/test/java/org/apache/shenyu/e2e/testcase/plugin/PluginsTest.java
@@ -37,11 +37,11 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@ShenYuTest(
- mode = Mode.HOST,
+ mode = Mode.DOCKER,
services = {
@ServiceConfigure(
serviceName = "admin",
- baseUrl = "http://{hostname:localhost}:9095",
+ port = 9095,
parameters = {
@Parameter(key = "username", value = "admin"),
@Parameter(key = "password", value = "123456"),
@@ -49,10 +49,11 @@ import org.junit.jupiter.api.BeforeEach;
),
@ServiceConfigure(
serviceName = "gateway",
- baseUrl = "http://{hostname:localhost}:9195",
+ port = 9195,
type = ServiceType.SHENYU_GATEWAY
)
- }
+ },
+ dockerComposeFile = "classpath:./docker-compose.yml"
)
public class PluginsTest {
@@ -81,8 +82,7 @@ public class PluginsTest {
@ShenYuScenario(provider = DividePluginCases.class)
void testDivide(GatewayClient gateway, CaseSpec spec) {
- spec.getVerifiers()
- .forEach(verifier ->
verifier.verify(gateway.getHttpRequesterSupplier().get()));
+ spec.getVerifiers().forEach(verifier ->
verifier.verify(gateway.getHttpRequesterSupplier().get()));
}
@AfterEach
diff --git
a/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.base.yml
b/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.base.yml
new file mode 100644
index 000000000..50a70bcfe
--- /dev/null
+++ b/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.base.yml
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+version: '2.3'
+
+services:
+ admin:
+ image: shenyu/admin:latest
+ expose:
+ - 9095
+ gateway:
+ image: shenyu/bootstrap:latest
+ expose:
+ - 9095
+ httpbin:
+ image: kennethreitz/httpbin:latest
+ expose:
+ - 80
+ mysql:
+ image: mysql:8
+ expose:
+ - 3306
diff --git a/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.yml
b/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.yml
new file mode 100644
index 000000000..bc7bcb118
--- /dev/null
+++ b/shenyu-e2e/shenyu-e2e-case/src/test/resources/docker-compose.yml
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+version: '2.3'
+
+services:
+ admin:
+ image: shenyu/admin:latest
+ expose:
+ - 9095
+
+ gateway:
+ image: shenyu/bootstrap:latest
+ environment:
+ - shenyu.sync.websocket.urls=ws://admin:9095/websocket
+ expose:
+ - 9095
+
+ httpbin:
+ image: kennethreitz/httpbin:latest
+ expose:
+ - 80
diff --git
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
index 99f1c357f..5e45e31ef 100644
---
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
+++
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/AdminClient.java
@@ -55,7 +55,6 @@ import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
@@ -126,11 +125,8 @@ public class AdminClient {
Assertions.assertNotNull(token, "checking token not null");
Assertions.assertNotEquals("", token, "checking token not empty");
basicAuth.set("X-Access-Token", token);
-
- Map<String, String> nameNidMap = Plugin.toMap();
- listPlugins().forEach(dto -> {
- Assertions.assertEquals(dto.getId(),
nameNidMap.get(dto.getName()), "checking Plugin[" + dto.getName() + "]'s id");
- });
+
+ Plugin.check(listPlugins());
}
public List<PluginDTO> listPlugins() {
@@ -210,7 +206,7 @@ public class AdminClient {
HttpEntity<SearchCondition> entity = new HttpEntity<>(searchCondition,
basicAuth);
ResponseEntity<ShenYuResult> response = template.postForEntity(baseURL
+ uri, entity, ShenYuResult.class);
- ShenYuResult rst = assertAndGet(response, "ok");
+ ShenYuResult rst = assertAndGet(response, "query success");
return Assertions.assertDoesNotThrow(
() -> mapper.readValue(rst.getData().traverse(), valueType),
diff --git
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/model/Plugin.java
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/model/Plugin.java
index b7dea76a1..2021d4cca 100644
---
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/model/Plugin.java
+++
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/client/admin/model/Plugin.java
@@ -18,11 +18,18 @@
package org.apache.shenyu.e2e.client.admin.model;
import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shenyu.e2e.client.admin.model.response.PluginDTO;
+import org.junit.jupiter.api.Assertions;
import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
+@Slf4j
public enum Plugin {
SIGN("sign", 1),
@@ -58,6 +65,9 @@ public enum Plugin {
LOGGING_ELASTIC_SEARCH("loggingElasticSearch", 32),
LOGGING_KAFKA("loggingKafka", 33),
LOGGING_ALIYUN_SLS("loggingAliyunSls", 34),
+ LOGGING_TENCENT_CLS("loggingTencentCls", 36),
+ LOGGING_PULSAR("loggingPulsar", 35),
+ LOGGING_CLICK_HOUSE("loggingClickHouse", 38),
;
private final String id;
@@ -80,4 +90,29 @@ public enum Plugin {
public static Map<String, String> toMap() {
return
Arrays.stream(Plugin.values()).collect(Collectors.toUnmodifiableMap(Plugin::getAlias,
Plugin::getId));
}
+
+ public static void check(List<PluginDTO> plugins) {
+ StringBuilder builder = new StringBuilder();
+ Map<String, String> pluginMap = toMap();
+ AssertionError error = null;
+
+ for (PluginDTO plugin : plugins) {
+ try {
+ Assertions.assertEquals(
+ plugin.getId(),
+ pluginMap.get(plugin.getName()),
+ "checking Plugin[" + plugin.getName() + "]'s id"
+ );
+ } catch (AssertionError e) {
+ String name = plugin.getName().replaceAll("(\\p{Lu})",
"_$1").toUpperCase(Locale.ROOT);
+ builder.append(System.lineSeparator())
+ .append(String.format("%s(\"%s\", %s),", name,
plugin.getName(), plugin.getId()));
+ error = e;
+ }
+ }
+ if (Objects.nonNull(error)) {
+ log.warn("please paste follow lines to
org.apache.shenyu.e2e.client.admin.model.Plugin:{}", builder);
+// throw error; // TODO Don't throw it temporarily
+ }
+ }
}
diff --git
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/matcher/SelectorMatcher.java
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/matcher/SelectorMatcher.java
index c2b451800..a77f361f1 100644
---
a/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/matcher/SelectorMatcher.java
+++
b/shenyu-e2e/shenyu-e2e-client/src/main/java/org/apache/shenyu/e2e/matcher/SelectorMatcher.java
@@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.apache.shenyu.e2e.client.admin.model.data.SelectorData;
import org.apache.shenyu.e2e.client.admin.model.response.SelectorDTO;
-import org.apache.shenyu.e2e.common.IdManagers.Plugins;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
diff --git
a/shenyu-e2e/shenyu-e2e-client/src/test/java/org/apache/shenyu/e2e/client/admin/AdminClientTest.java
b/shenyu-e2e/shenyu-e2e-client/src/test/java/org/apache/shenyu/e2e/client/admin/AdminClientTest.java
index 188f8ee55..c51ce1315 100644
---
a/shenyu-e2e/shenyu-e2e-client/src/test/java/org/apache/shenyu/e2e/client/admin/AdminClientTest.java
+++
b/shenyu-e2e/shenyu-e2e-client/src/test/java/org/apache/shenyu/e2e/client/admin/AdminClientTest.java
@@ -57,7 +57,7 @@ import java.util.stream.Collectors;
public class AdminClientTest {
static AdminClient client;
- static GenericContainer<?> container = new
GenericContainer<>("ghcr.io/apache/incubator-shenyu/admin:782867187a76f8a273ccc8029e53e3db3f23eb6b")
+ static GenericContainer<?> container = new
GenericContainer<>("ghcr.io/apache/shenyu/admin:latest")
.withExposedPorts(9095)
.withLogConsumer(new Slf4jLogConsumer(log));
diff --git
a/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.base.yaml
b/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.base.yaml
deleted file mode 100644
index 7810d17b0..000000000
--- a/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.base.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
-~ 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.
--->
diff --git a/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.yml
b/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.yml
deleted file mode 100644
index b75ac3d32..000000000
--- a/shenyu-e2e/shenyu-e2e-client/src/test/resources/docker-compose.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ 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.
- -->
-
-version: '2.3'
-
-services:
- admin:
- image:
ghcr.io/apache/incubator-shenyu/admin:782867187a76f8a273ccc8029e53e3db3f23eb6b
- expose:
- - 9095
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/DockerServiceCompose.java
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/DockerServiceCompose.java
index 8d940853a..65e50bc88 100644
---
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/DockerServiceCompose.java
+++
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/DockerServiceCompose.java
@@ -17,23 +17,22 @@
package org.apache.shenyu.e2e.engine.service;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import junit.framework.AssertionFailedError;
import lombok.Getter;
-import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.shenyu.e2e.client.ExternalServiceClient;
import org.apache.shenyu.e2e.client.admin.AdminClient;
import org.apache.shenyu.e2e.client.gateway.GatewayClient;
+import org.apache.shenyu.e2e.common.TableView;
import
org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.DockerConfigure;
import
org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.DockerConfigure.DockerServiceConfigure;
import org.apache.shenyu.e2e.engine.service.docker.DockerComposeFile;
import org.apache.shenyu.e2e.engine.service.docker.ShenYuLogConsumer;
-import org.apache.shenyu.e2e.common.TableView;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.DockerComposeContainer;
-import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
-import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -47,6 +46,8 @@ public class DockerServiceCompose implements ServiceCompose {
private final DockerServiceConfigure adminConfigure;
private final DockerServiceConfigure gatewayConfigure;
+ private List<DockerServiceConfigure> externalServiceConfigurations;
+
public DockerServiceCompose(DockerConfigure configure) {
this.configure = configure;
this.adminConfigure = configure.getAdmin();
@@ -56,25 +57,54 @@ public class DockerServiceCompose implements ServiceCompose
{
container = new DockerComposeContainer<>("e2e",
parsedDockerComposeFile.getFile());
List<String> services = parsedDockerComposeFile.getServices();
- services.forEach(name -> container.withLogConsumer(name, new
ShenYuLogConsumer()));
+ services.forEach(name -> container.withLogConsumer(name, new
ShenYuLogConsumer(name)));
}
- @SneakyThrows
public void start() {
- List<DockerServiceConfigure> externalServices =
configure.getExternalServices();
+ exposedServices();
+ waitingForAvailable();
+
+ container.start();
+
+ NamingResolver.INSTANCE.ofDockerConfigure(container);
+ printServices();
+ }
+
+ private void exposedServices() {
+ Builder<DockerServiceConfigure> builder =
ImmutableList.<DockerServiceConfigure>builder()
+ .addAll(configure.getExternalServices());
if (Objects.nonNull(adminConfigure)) {
- externalServices.add(adminConfigure);
+ builder.add(adminConfigure);
}
if (Objects.nonNull(gatewayConfigure)) {
- externalServices.add(gatewayConfigure);
+ builder.add(gatewayConfigure);
}
- externalServices.stream().filter(conf -> conf.getPort() > 1024)
- .forEach(conf ->
container.withExposedService(conf.getServiceName(), conf.getPort()));
-
- container.start();
+ externalServiceConfigurations = builder.build();
+ externalServiceConfigurations.stream()
+ .filter(conf -> conf.getPort() > 1024)
+ .forEach(conf ->
container.withExposedService(conf.getServiceName(), conf.getPort()));
+ }
+
+ private void waitingForAvailable() {
+ if (Objects.nonNull(adminConfigure)) {
+ container.waitingFor(
+ adminConfigure.getServiceName(),
+
WaitingForStrategies.newAdminStrategy(adminConfigure.getPort())
+ );
+ }
+ if (Objects.nonNull(gatewayConfigure)) {
+ container.waitingFor(
+ gatewayConfigure.getServiceName(),
+
WaitingForStrategies.newGatewayStrategy(gatewayConfigure.getPort())
+ );
+ }
+
+ }
+
+ private void printServices() {
TableView tableView = new TableView("service name", "container port",
"mapped host port");
- for (DockerServiceConfigure serviceConfigure : externalServices) {
+ for (DockerServiceConfigure serviceConfigure :
externalServiceConfigurations) {
Optional<ContainerState> stateOpt =
container.getContainerByServiceName(serviceConfigure.getServiceName());
if (stateOpt.isPresent()) {
ContainerState state = stateOpt.get();
@@ -90,33 +120,7 @@ public class DockerServiceCompose implements ServiceCompose
{
}
}
log.info(System.lineSeparator() + tableView.printAsString() +
System.lineSeparator());
-
- if (Objects.nonNull(adminConfigure)) {
- container.waitingFor(
- adminConfigure.getServiceName(),
- new HttpWaitStrategy()
- .allowInsecure()
- .forPort(adminConfigure.getPort())
- .withMethod("GET")
- .forPath("/actuator")
- .forStatusCode(200)
- .withReadTimeout(Duration.ofMinutes(1))
- .withStartupTimeout(Duration.ofMinutes(3))
- );
- }
- if (Objects.nonNull(gatewayConfigure)) {
- container.waitingFor(
- gatewayConfigure.getServiceName(),
- new HttpWaitStrategy()
- .allowInsecure()
- .forPort(gatewayConfigure.getPort())
- .withMethod("GET")
- .forPath("/actuator")
- .forStatusCode(200)
- .withReadTimeout(Duration.ofMinutes(1))
- .withStartupTimeout(Duration.ofMinutes(3))
- );
- }
+
if (Objects.isNull(adminConfigure) &&
Objects.isNull(gatewayConfigure)) {
log.warn("configure of shenyu-admin or shenyu-bootstrap(gateway)
has not seen");
}
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/HostServiceCompose.java
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/HostServiceCompose.java
index 812be6d64..50684eb9c 100644
---
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/HostServiceCompose.java
+++
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/HostServiceCompose.java
@@ -17,6 +17,7 @@
package org.apache.shenyu.e2e.engine.service;
+import com.google.common.collect.Lists;
import junit.framework.AssertionFailedError;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -26,6 +27,9 @@ import org.apache.shenyu.e2e.client.gateway.GatewayClient;
import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.HostConfigure;
import
org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.HostConfigure.HostServiceConfigure;
+import java.util.List;
+import java.util.Objects;
+
@Getter
@AllArgsConstructor
public class HostServiceCompose implements ServiceCompose {
@@ -33,7 +37,14 @@ public class HostServiceCompose implements ServiceCompose {
private HostConfigure configure;
public void start() {
-
+ List<HostServiceConfigure> configures =
Lists.newArrayList(configure.getExternalServices());
+ if (Objects.nonNull(configure.getAdmin())) {
+ configures.add(configure.getAdmin());
+ }
+ if (Objects.nonNull(configure.getGateway())) {
+ configures.add(configure.getGateway());
+ }
+ NamingResolver.INSTANCE.ofHostConfigure(configures);
}
@Override
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/NamingResolver.java
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/NamingResolver.java
new file mode 100644
index 000000000..8bc7b34fe
--- /dev/null
+++
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/NamingResolver.java
@@ -0,0 +1,111 @@
+/*
+ * 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.e2e.engine.service;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import lombok.extern.slf4j.Slf4j;
+import
org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.HostConfigure.HostServiceConfigure;
+import org.testcontainers.containers.DockerComposeContainer;
+
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+public enum NamingResolver {
+ INSTANCE;
+
+ private Map<String, String> namingMap;
+
+ public void ofHostConfigure(List<HostServiceConfigure> serviceConfigures) {
+ namingMap = serviceConfigures.stream()
+ .collect(Collectors.toUnmodifiableMap(
+ HostServiceConfigure::getServiceName,
+ c -> getAddressFromBaseUrl(c.getBaseUrl())
+ )
+ );
+ }
+
+ public void ofDockerConfigure(DockerComposeContainer<?> container) {
+ final Map<String, String> _namingMap = Maps.newHashMap();
+ try {
+ Field field =
container.getClass().getDeclaredField("serviceInstanceMap");
+ field.setAccessible(true);
+ Map<String, ?> serviceInstanceMap = (Map<String, ?>)
field.get(container);
+
+ serviceInstanceMap.keySet().forEach(e -> {
+ container.getContainerByServiceName(e).ifPresentOrElse(
+ c -> {
+
c.getContainerInfo().getNetworkSettings().getNetworks().entrySet()
+ .stream()
+ .findFirst()
+ .ifPresent(net -> {
+ String ip =
net.getValue().getIpAddress();
+
net.getValue().getAliases().forEach(alias -> _namingMap.put(alias, ip));
+ });
+ },
+ () -> log.warn("service {} not exists", e)
+ );
+ });
+ System.out.println(_namingMap);
+ } catch (NoSuchFieldException | IllegalAccessException ignore) {
+ }
+ this.namingMap = ImmutableMap.<String,
String>builder().putAll(_namingMap).build();
+ }
+
+ private static String getAddressFromBaseUrl(String baseUrl) {
+ String address = baseUrl.replaceFirst("http(s)*://", "");
+ if (address.contains("/")) {
+ int index = address.indexOf('/');
+ address = address.substring(0, index);
+ }
+ return address;
+ }
+
+ /**
+ * resolve name to ip address.
+ *
+ * @param name represents serviceName, hostname, or domain.
+ * @return return resulted ip if resolved success. otherwise, return name.
+ */
+ public String resolve(String name) {
+ String rst = namingMap.get(name);
+ if (!Strings.isNullOrEmpty(rst)) {
+ log.info("resolved {} to {} by docker-compose", name, rst);
+ return rst;
+ }
+
+ try {
+ InetAddress address = InetAddress.getByName(name);
+ rst = address.getHostAddress();
+ if (!Strings.isNullOrEmpty(rst)) {
+ log.info("resolved {} to {} by InetAddress", name, rst);
+ return rst;
+ }
+ } catch (UnknownHostException ignore) {
+ }
+ log.info("failed to resolve {}", name);
+
+ return name;
+ }
+}
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/WaitingForStrategies.java
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/WaitingForStrategies.java
new file mode 100644
index 000000000..f6cd9e080
--- /dev/null
+++
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/WaitingForStrategies.java
@@ -0,0 +1,49 @@
+/*
+ * 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.e2e.engine.service;
+
+import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
+import org.testcontainers.containers.wait.strategy.WaitStrategy;
+
+import java.time.Duration;
+
+public class WaitingForStrategies {
+
+ public static WaitStrategy newAdminStrategy(int port) {
+ return new HttpWaitStrategy()
+ .allowInsecure()
+ .forPort(port)
+ .withMethod("GET")
+ .forPath("/actuator/health")
+ .forStatusCode(200)
+ .withReadTimeout(Duration.ofSeconds(3))
+ .withStartupTimeout(Duration.ofMinutes(3));
+ }
+
+ public static WaitStrategy newGatewayStrategy(int port) {
+ return new HttpWaitStrategy()
+ .allowInsecure()
+ .forPort(port)
+ .withMethod("GET")
+ .forPath("/actuator/health")
+ .forStatusCode(200)
+ .withReadTimeout(Duration.ofSeconds(3))
+ .withStartupTimeout(Duration.ofMinutes(3));
+ }
+
+}
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/docker/ShenYuLogConsumer.java
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/docker/ShenYuLogConsumer.java
index ef37151dd..c4e075ffd 100644
---
a/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/docker/ShenYuLogConsumer.java
+++
b/shenyu-e2e/shenyu-e2e-engine/src/main/java/org/apache/shenyu/e2e/engine/service/docker/ShenYuLogConsumer.java
@@ -23,8 +23,9 @@ import org.testcontainers.containers.output.Slf4jLogConsumer;
@Slf4j(topic = "docker-compose")
public class ShenYuLogConsumer extends Slf4jLogConsumer {
- public ShenYuLogConsumer() {
+ public ShenYuLogConsumer(String serviceName) {
super(log);
+ withPrefix("service_name:" + serviceName);
}
}
diff --git
a/shenyu-e2e/shenyu-e2e-engine/src/test/java/org/apache/shenyu/e2e/engine/ShenYuEngineTest.java
b/shenyu-e2e/shenyu-e2e-engine/src/test/java/org/apache/shenyu/e2e/engine/ShenYuEngineTest.java
index 50d2add47..4b480950d 100644
---
a/shenyu-e2e/shenyu-e2e-engine/src/test/java/org/apache/shenyu/e2e/engine/ShenYuEngineTest.java
+++
b/shenyu-e2e/shenyu-e2e-engine/src/test/java/org/apache/shenyu/e2e/engine/ShenYuEngineTest.java
@@ -17,7 +17,6 @@
package org.apache.shenyu.e2e.engine;
-
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.shenyu.e2e.client.admin.AdminClient;
import
org.apache.shenyu.e2e.client.admin.model.data.SearchCondition.SelectorQueryCondition;
@@ -26,20 +25,28 @@ import org.apache.shenyu.e2e.engine.annotation.ShenYuTest;
import org.apache.shenyu.e2e.engine.annotation.ShenYuTest.Parameter;
import org.apache.shenyu.e2e.engine.annotation.ShenYuTest.ServiceConfigure;
import org.apache.shenyu.e2e.engine.config.ShenYuEngineConfigure.Mode;
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
+import org.testcontainers.containers.DockerComposeContainer;
+import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
+import java.io.File;
+import java.net.URL;
+import java.time.Duration;
import java.util.List;
+@Disabled
@ShenYuTest(
mode = Mode.HOST,
services = {
@ServiceConfigure(
serviceName = "admin",
- baseUrl = "http://localhost:9095",
+ baseUrl = "http://localhost:19095",
parameters = {
@Parameter(key = "username", value = "admin"),
@Parameter(key = "password", value = "123456"),
@@ -49,6 +56,22 @@ import java.util.List;
)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ShenYuEngineTest {
+ private static DockerComposeContainer<?> compose = null;
+ static {
+ URL resource
=ShenYuEngineTest.class.getResource("/docker-compose.yml");
+ compose = new DockerComposeContainer<>("test", new
File(resource.getPath()));
+ compose.withExposedService("admin", 9095);
+ compose.waitingFor("admin", new HttpWaitStrategy()
+ .allowInsecure()
+ .forPort(9095)
+ .withMethod("GET")
+ .forPath("/platform/login")
+ .forStatusCode(200)
+ .forResponsePredicate(body -> body.contains("username or
password error"))
+ .withReadTimeout(Duration.ofMinutes(1))
+ .withStartupTimeout(Duration.ofMinutes(3)));
+ compose.start();
+ }
@BeforeAll
static void setup(AdminClient client) {
@@ -81,4 +104,9 @@ public class ShenYuEngineTest {
void testListRules(AdminClient client) {
client.listAllRules().forEach(e -> client.deleteRules(e.getId()));
}
+
+ @AfterAll
+ static void teardown() {
+ compose.close();
+ }
}
diff --git a/shenyu-e2e/shenyu-e2e-engine/src/test/resources/docker-compose.yml
b/shenyu-e2e/shenyu-e2e-engine/src/test/resources/docker-compose.yml
new file mode 100644
index 000000000..d2773386b
--- /dev/null
+++ b/shenyu-e2e/shenyu-e2e-engine/src/test/resources/docker-compose.yml
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+version: '2.3'
+
+services:
+ admin:
+ image: ghcr.io/apache/shenyu/admin:latest
+ ports:
+ - "19095:9095"