From 6c22db2e36c92ad2405d3e493598db0cde98d10f Mon Sep 17 00:00:00 2001 From: spacewander Date: Thu, 28 Nov 2024 16:19:59 +0800 Subject: [PATCH] test: support running integration test with envoy bin Signed-off-by: spacewander --- .github/workflows/test.yml | 6 + api/Makefile | 14 + .../integration/dataplane/binary_mode.go | 29 ++ .../tests/integration/dataplane/bootstrap.go | 37 ++- .../tests/integration/dataplane/data_plane.go | 278 +++++++++++------- .../tests/integration/test_binary_mode.sh | 19 ++ .../tests/integration/testdata/Dockerfile | 25 ++ .../plugin_integration_test_framework.md | 8 + .../plugin_integration_test_framework.md | 8 + 9 files changed, 320 insertions(+), 104 deletions(-) create mode 100644 api/plugins/tests/integration/dataplane/binary_mode.go create mode 100755 api/plugins/tests/integration/test_binary_mode.sh create mode 100644 api/plugins/tests/integration/testdata/Dockerfile diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8be66c4a..c5eb78ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,6 +101,12 @@ jobs: go install github.com/codesenberg/bombardier@latest HTNN_DATA_PLANE_BENCHMARK_DURATION=1s make benchmark + # this part is envoy version agnostic so we only run it once + - if: ${{ matrix.envoy_version == 'dev' }} + name: Test plugin integration test framework + run: | + make test-integration-framework-in-docker + types-module-test: timeout-minutes: 10 runs-on: ubuntu-latest diff --git a/api/Makefile b/api/Makefile index 0e0c86c4..e45df97f 100644 --- a/api/Makefile +++ b/api/Makefile @@ -52,6 +52,20 @@ integration-test: go test -tags envoy${ENVOY_API_VERSION} -v ${PKG} || exit 1; \ ) +.PHONY: test-integration-framework-in-docker +test-integration-framework-in-docker: + docker build -t test-integration-framework-in-docker \ + --build-arg PROXY_BASE_IMAGE=${PROXY_IMAGE} \ + --build-arg GO_BUILD_BASE_IMAGE=${BUILD_IMAGE} \ + plugins/tests/integration/testdata + docker run --rm ${MOUNT_GOMOD_CACHE} \ + -v $(PWD)/..:/go/src/${PROJECT_NAME} \ + -w /go/src/${PROJECT_NAME}/api \ + -e GOPROXY \ + -e ENVOY_API_VERSION \ + test-integration-framework-in-docker \ + /go/src/${PROJECT_NAME}/api/plugins/tests/integration/test_binary_mode.sh + # The benchmark running time can be controlled via env var HTNN_DATA_PLANE_BENCHMARK_DURATION .PHONY: benchmark benchmark: diff --git a/api/plugins/tests/integration/dataplane/binary_mode.go b/api/plugins/tests/integration/dataplane/binary_mode.go new file mode 100644 index 00000000..a446ba17 --- /dev/null +++ b/api/plugins/tests/integration/dataplane/binary_mode.go @@ -0,0 +1,29 @@ +// Copyright The HTNN Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dataplane + +import "os" + +var ( + binaryPath = "" +) + +func init() { + binaryPath = os.Getenv("TEST_ENVOY_BINARY_PATH") +} + +func isBinaryMode() bool { + return binaryPath != "" +} diff --git a/api/plugins/tests/integration/dataplane/bootstrap.go b/api/plugins/tests/integration/dataplane/bootstrap.go index 21ffad7e..5c999145 100644 --- a/api/plugins/tests/integration/dataplane/bootstrap.go +++ b/api/plugins/tests/integration/dataplane/bootstrap.go @@ -19,6 +19,7 @@ import ( "encoding/json" "math/rand" "os" + "strconv" "gopkg.in/yaml.v3" ) @@ -34,6 +35,8 @@ type bootstrap struct { httpFilterGolang map[string]interface{} accessLogFormat string clusters []map[string]interface{} + + dp *DataPlane } func Bootstrap() *bootstrap { @@ -44,6 +47,10 @@ func Bootstrap() *bootstrap { } } +func (b *bootstrap) SetDataPlane(dp *DataPlane) { + b.dp = dp +} + func (b *bootstrap) AddBackendRoute(s string) *bootstrap { var n map[string]interface{} err := yaml.Unmarshal([]byte(s), &n) @@ -150,15 +157,19 @@ func (b *bootstrap) buildConfiguration() (map[string]interface{}, error) { staticResources := root["static_resources"].(map[string]interface{}) clusters := staticResources["clusters"].([]interface{}) - port := "9999" - portEnv := os.Getenv("TEST_ENVOY_CONTROL_PLANE_PORT") - if portEnv != "" { + port := 9999 + portEnv, _ := strconv.Atoi(os.Getenv("TEST_ENVOY_CONTROL_PLANE_PORT")) + if portEnv != 0 { port = portEnv } for _, c := range clusters { if c.(map[string]interface{})["name"] == "config_server" { load := c.(map[string]interface{})["load_assignment"].(map[string]interface{})["endpoints"].([]interface{})[0].(map[string]interface{})["lb_endpoints"].([]interface{})[0].(map[string]interface{})["endpoint"].(map[string]interface{})["address"].(map[string]interface{})["socket_address"].(map[string]interface{}) load["port_value"] = port + + if isBinaryMode() { + load["address"] = "0.0.0.0" + } break } } @@ -168,6 +179,26 @@ func (b *bootstrap) buildConfiguration() (map[string]interface{}, error) { newClusters = append(newClusters, c) } staticResources["clusters"] = append(clusters, newClusters...) + + addr := root["admin"].(map[string]interface{})["address"].(map[string]interface{})["socket_address"].(map[string]interface{}) + addr["port_value"] = b.dp.AdminAPIPort() + addr = staticResources["listeners"].([]interface{})[0].(map[string]interface{})["address"].(map[string]interface{})["socket_address"].(map[string]interface{}) + addr["port_value"] = b.dp.Port() + + if isBinaryMode() { + for _, l := range root["static_resources"].(map[string]interface{})["listeners"].([]interface{}) { + listener := l.(map[string]interface{}) + hcm := listener["filter_chains"].([]interface{})[0].(map[string]interface{})["filters"].([]interface{})[0].(map[string]interface{})["typed_config"].(map[string]interface{}) + httpFilters := hcm["http_filters"].([]interface{}) + for _, hf := range httpFilters { + cfg := hf.(map[string]interface{})["typed_config"].(map[string]interface{}) + if cfg["@type"] == "type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config" { + cfg["library_path"] = b.dp.soPath + } + } + } + } + return root, nil } diff --git a/api/plugins/tests/integration/dataplane/data_plane.go b/api/plugins/tests/integration/dataplane/data_plane.go index 55839c07..bf7ea9d2 100644 --- a/api/plugins/tests/integration/dataplane/data_plane.go +++ b/api/plugins/tests/integration/dataplane/data_plane.go @@ -32,6 +32,7 @@ import ( "runtime" "strconv" "strings" + "syscall" "testing" "time" @@ -60,6 +61,7 @@ type DataPlane struct { dataPlanePort string adminAPIPort string + soPath string } type Option struct { @@ -72,6 +74,12 @@ type Option struct { ExpectNoLogPattern []string } +func addEnvironemntVariables(cmd *exec.Cmd, envs map[string]string) { + for k, v := range envs { + cmd.Env = append(cmd.Env, k+"="+v) + } +} + func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { if opt == nil { opt = &Option{} @@ -87,6 +95,8 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { t: t, opt: opt, } + opt.Bootstrap.SetDataPlane(dp) + err := dp.cleanup(t) if err != nil { return nil, err @@ -97,47 +107,6 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { return nil, err } - cfgFilename := "envoy.yaml" - cfgFile, err := os.Create(filepath.Join(dir, cfgFilename)) - if err != nil { - return nil, err - } - - err = opt.Bootstrap.WriteTo(cfgFile) - cfgFile.Close() - if err != nil { - return nil, err - } - - envoyCmd := "envoy -c /etc/envoy.yaml" - envoyValidateCmd := envoyCmd + " --mode validate -l critical" - if opt.LogLevel != "" { - envoyCmd += " -l " + opt.LogLevel - } - - hostAddr := "" - if runtime.GOOS == "linux" { - // We use this special domain to access the control plane on host. - // It works with Docker for Win/Mac (--network host doesn't work). - // For Linux's Docker, a special option is used instead - hostAddr = "--add-host=host.docker.internal:host-gateway" - } - - currentUser, err := user.Current() - if err != nil { - return nil, err - } - - networkName := "services_service" - err = exec.Command("docker", "network", "inspect", networkName).Run() - if err != nil { - logger.Info("docker network used by test not found, create one") - err = exec.Command("docker", "network", "create", networkName).Run() - if err != nil { - return nil, err - } - } - coverDir := helper.CoverDir() _, err = os.Stat(coverDir) if err != nil { @@ -151,33 +120,6 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { // Since we only care about the coverage in CI, it is fine so far. } - image := "m.daocloud.io/docker.io/envoyproxy/envoy:contrib-v1.32.0" - - specifiedImage := os.Getenv("PROXY_IMAGE") - if specifiedImage != "" { - image = specifiedImage - } - - b, err := exec.Command("docker", "images", image).Output() - if err != nil { - return nil, err - } - if len(strings.Split(string(b), "\n")) < 3 { - cmd := exec.Command("docker", "pull", image) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - logger.Info("pull envoy image", "cmdline", cmd.String()) - err = cmd.Run() - if err != nil { - return nil, err - } - } - - envs := []string{} - for k, v := range opt.Envs { - envs = append(envs, "-e", k+"="+v) - } - pwd, _ := os.Getwd() soPath := filepath.Join(pwd, "libgolang.so") st, err := os.Stat(soPath) @@ -194,6 +136,7 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { "shared library path", soPath) return nil, err } + dp.soPath = soPath adminAPIPort := "9998" adminAPIPortEnv := os.Getenv("TEST_ENVOY_ADMIN_API_PORT") @@ -209,19 +152,99 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { } dp.dataPlanePort = dataPlanePort - cmdline := "docker run" + - " --name " + containerName + - " --network " + networkName + - " --user " + currentUser.Uid + - " --rm -t -v " + - cfgFile.Name() + ":/etc/envoy.yaml -v " + - soPath + ":/etc/libgolang.so" + - " -v /tmp:/tmp" + - " -e GOCOVERDIR=" + coverDir + - " " + strings.Join(envs, " ") + - " -p " + dataPlanePort + ":10000 -p " + adminAPIPort + ":9998 " + - hostAddr + " " + - image + cfgFilename := "envoy.yaml" + cfgFile, err := os.Create(filepath.Join(dir, cfgFilename)) + if err != nil { + return nil, err + } + + err = opt.Bootstrap.WriteTo(cfgFile) + cfgFile.Close() + if err != nil { + return nil, err + } + + var cmdline, envoyCmd string + + if isBinaryMode() { + envoyCmd = binaryPath + " -c " + cfgFile.Name() + + if len(opt.Envs) == 0 { + opt.Envs = map[string]string{} + } + opt.Envs["GOCOVERDIR"] = coverDir + + } else { + hostAddr := "" + if runtime.GOOS == "linux" { + // We use this special domain to access the control plane on host. + // It works with Docker for Win/Mac (--network host doesn't work). + // For Linux's Docker, a special option is used instead + hostAddr = "--add-host=host.docker.internal:host-gateway" + } + + currentUser, err := user.Current() + if err != nil { + return nil, err + } + + networkName := "services_service" + err = exec.Command("docker", "network", "inspect", networkName).Run() + if err != nil { + logger.Info("docker network used by test not found, create one") + err = exec.Command("docker", "network", "create", networkName).Run() + if err != nil { + return nil, err + } + } + + image := "m.daocloud.io/docker.io/envoyproxy/envoy:contrib-v1.32.0" + + specifiedImage := os.Getenv("PROXY_IMAGE") + if specifiedImage != "" { + image = specifiedImage + } + + b, err := exec.Command("docker", "images", image).Output() + if err != nil { + return nil, err + } + if len(strings.Split(string(b), "\n")) < 3 { + cmd := exec.Command("docker", "pull", image) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + logger.Info("pull envoy image", "cmdline", cmd.String()) + err = cmd.Run() + if err != nil { + return nil, err + } + } + + envs := []string{} + for k, v := range opt.Envs { + envs = append(envs, "-e", k+"="+v) + } + + cmdline = "docker run" + + " --name " + containerName + + " --network " + networkName + + " --user " + currentUser.Uid + + " --rm -t -v " + + cfgFile.Name() + ":/etc/envoy.yaml -v " + + soPath + ":/etc/libgolang.so" + + " -v /tmp:/tmp" + + " -e GOCOVERDIR=" + coverDir + + " " + strings.Join(envs, " ") + + " -p " + dataPlanePort + ":" + dataPlanePort + + " -p " + adminAPIPort + ":" + adminAPIPort + " " + + hostAddr + " " + + image + + envoyCmd = "envoy -c /etc/envoy.yaml" + } + + // show why the configuration is invalid + envoyValidateCmd := envoyCmd + " --mode validate -l error" content, _ := os.ReadFile(cfgFile.Name()) digest := md5.Sum(content) @@ -234,7 +257,13 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { validateCmd := cmdline + " " + envoyValidateCmd cmds := strings.Fields(validateCmd) logger.Info("run validate cmd", "cmdline", validateCmd) - out, err := exec.Command(cmds[0], cmds[1:]...).CombinedOutput() + + cmd := exec.Command(cmds[0], cmds[1:]...) + if isBinaryMode() { + addEnvironemntVariables(cmd, opt.Envs) + } + + out, err := cmd.CombinedOutput() if err != nil { logger.Info("bad envoy bootstrap configuration", "cmd", validateCmd, "output", string(out)) return nil, err @@ -246,24 +275,49 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { cfgFile.Write(content) } + if opt.LogLevel != "" { + envoyCmd += " -l " + opt.LogLevel + } + cmdline = cmdline + " " + envoyCmd logger.Info("run cmd", "cmdline", cmdline) cmds := strings.Fields(cmdline) cmd := exec.Command(cmds[0], cmds[1:]...) - - stdout, err := os.Create(filepath.Join(dir, "stdout")) - if err != nil { - return nil, err + if isBinaryMode() { + addEnvironemntVariables(cmd, opt.Envs) } - cmd.Stdout = stdout - stderr, err := os.Create(filepath.Join(dir, "stderr")) - if err != nil { - return nil, err + if isBinaryMode() { + // Like the standard mode, we use stdout file to store the log of Envoy + stdout, err := os.Create(filepath.Join(dir, "stdout")) + if err != nil { + return nil, err + } + cmd.Stdout = stdout + + // We don't need stderr file, which is used to store docker output in the standard mode. + cmd.Stderr = stdout + // Just left an empty file here to keep the same structure. + _, err = os.Create(filepath.Join(dir, "stderr")) + if err != nil { + return nil, err + } + } else { + stdout, err := os.Create(filepath.Join(dir, "stdout")) + if err != nil { + return nil, err + } + cmd.Stdout = stdout + + stderr, err := os.Create(filepath.Join(dir, "stderr")) + if err != nil { + return nil, err + } + cmd.Stderr = stderr } - cmd.Stderr = stderr + dp.cmd = cmd done := make(chan error) @@ -277,7 +331,19 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { go func() { done <- cmd.Wait() }() }() - helper.WaitServiceUp(t, ":"+dataPlanePort, "") + if isBinaryMode() { + // In binary mode, the port is open only after the control plane is up, which is called after + // the data plane is up. So we don't check if the port is open. Instead, we wait for a while + // to ensure the data plane can be started. + waitTime := 1 * time.Second + waitTimeEnv, _ := time.ParseDuration(os.Getenv("TEST_ENVOY_WAIT_BINARY_TO_START_TIME")) + if waitTimeEnv != 0 { + waitTime = waitTimeEnv + } + time.Sleep(waitTime) + } else { + helper.WaitServiceUp(t, ":"+dataPlanePort, "") + } select { case err := <-done: @@ -297,10 +363,12 @@ func (dp *DataPlane) root() string { return dir } -func (dp *DataPlane) cleanup(t *testing.T) error { - cmd := exec.Command("docker", "stop", containerName) - // ignore error when the containerName is not left over - _ = cmd.Run() +func (dp *DataPlane) cleanup(_ *testing.T) error { + if !isBinaryMode() { + cmd := exec.Command("docker", "stop", containerName) + // ignore error when the containerName is not left over + _ = cmd.Run() + } dir := dp.root() _, err := os.Stat(dir) @@ -325,8 +393,13 @@ func (dp *DataPlane) Stop() { } logger.Info("stop envoy") - cmd := exec.Command("docker", "stop", containerName) - err = cmd.Run() + + if isBinaryMode() { + dp.cmd.Process.Signal(syscall.SIGTERM) + } else { + cmd := exec.Command("docker", "stop", containerName) + err = cmd.Run() + } if err != nil { logger.Error(err, "failed to terminate envoy") return @@ -336,9 +409,12 @@ func (dp *DataPlane) Stop() { <-dp.done logger.Info("envoy stopped") - f := dp.cmd.Stderr.(*os.File) - f.Close() - f = dp.cmd.Stdout.(*os.File) + if !isBinaryMode() { + f := dp.cmd.Stderr.(*os.File) + f.Close() + } + + f := dp.cmd.Stdout.(*os.File) f.Seek(0, 0) text, err := io.ReadAll(f) defer f.Close() diff --git a/api/plugins/tests/integration/test_binary_mode.sh b/api/plugins/tests/integration/test_binary_mode.sh new file mode 100755 index 00000000..880fe165 --- /dev/null +++ b/api/plugins/tests/integration/test_binary_mode.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Copyright The HTNN Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +echo "Running tests in binary mode" +TEST_ENVOY_BINARY_PATH=$(which envoy) go test -tags "envoy$(ENVOY_API_VERSION)" -v mosn.io/htnn/api/tests/integration -run TestFilterManagerEncode$ diff --git a/api/plugins/tests/integration/testdata/Dockerfile b/api/plugins/tests/integration/testdata/Dockerfile new file mode 100644 index 00000000..a6ec83a8 --- /dev/null +++ b/api/plugins/tests/integration/testdata/Dockerfile @@ -0,0 +1,25 @@ +# Copyright The HTNN Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Dockerfile has specific requirement to put this ARG at the beginning: +# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact +ARG GO_BUILD_BASE_IMAGE +ARG PROXY_BASE_IMAGE + +# hadolint ignore=DL3006 +FROM ${PROXY_BASE_IMAGE} as envoy +# hadolint ignore=DL3006 +FROM ${GO_BUILD_BASE_IMAGE} +COPY --from=envoy /usr/local/bin/envoy /usr/local/bin/envoy +ENTRYPOINT ["bash"] diff --git a/site/content/en/docs/developer-guide/plugin_integration_test_framework.md b/site/content/en/docs/developer-guide/plugin_integration_test_framework.md index 77019a2e..ea7c1481 100644 --- a/site/content/en/docs/developer-guide/plugin_integration_test_framework.md +++ b/site/content/en/docs/developer-guide/plugin_integration_test_framework.md @@ -18,6 +18,14 @@ By default, the test framework starts Envoy using the image `envoyproxy/envoy:co You may have noticed that when executing `go test`, we added `-tags envoy1.29`. This is because there are interface differences across different versions of Envoy. In this case, we specified the label for Envoy version 1.29. See [HTNN's Envoy multi-version support](./dataplane_support.md) for details. Note that the version of Envoy being run, the `-tags` parameter in the `go test` command, and the version of the Envoy interface that is depended upon when running `make build-test-so` should be consistent. +We can also start Envoy via binary (also known as binary mode). Using binary mode requires configuring the environment variable `TEST_ENVOY_BINARY_PATH` to point to the path of the Envoy binary file. For example, `TEST_ENVOY_BINARY_PATH=$(which envoy) go test -v ./tests/integration -run TestPluginXX`. Note that the Envoy binary and the Go plugin compiled so files need to be compatible: + +* The compilation platforms must be consistent +* The glibc versions must be compatible +* The Envoy API versions used must be consistent (see [HTNN's Envoy multi-version support](./dataplane_support.md)) + +By default, in binary mode, the testing framework will wait 1 second for Envoy to start. This time can be modified via the environment variable `TEST_ENVOY_WAIT_BINARY_TO_START_TIME`. For example, `TEST_ENVOY_BINARY_MODE_WAIT_TIME=2s TEST_ENVOY_BINARY_PATH=$(which envoy) go test -v ./tests/integration -run TestFilterManagerEncode`. + ## Port usage The test framework will occupy the following ports on the host machine: diff --git a/site/content/zh-hans/docs/developer-guide/plugin_integration_test_framework.md b/site/content/zh-hans/docs/developer-guide/plugin_integration_test_framework.md index a1b41f97..c0da4eab 100644 --- a/site/content/zh-hans/docs/developer-guide/plugin_integration_test_framework.md +++ b/site/content/zh-hans/docs/developer-guide/plugin_integration_test_framework.md @@ -18,6 +18,14 @@ title: 插件集成测试框架 您可能已经注意到,在执行 `go test` 时,我们添加了 `-tags envoy1.29`。这是因为不同版本 Envoy 接口存在差异。在这种情况下,我们指定了 Envoy 1.29 版本的标签。具体见 [HTNN 的 Envoy 多版本支持](./dataplane_support.md)。注意运行的 Envoy 版本,以及 `go test` 命令中的 `-tags` 参数,和 `make build-test-so` 时依赖的 Envoy 接口版本应该保持一致。 +我们也可以通过二进制来启动 Envoy(也即 binary mode)。使用 binary mode 需要配置环境变量 `TEST_ENVOY_BINARY_PATH`,指向 Envoy 的二进制文件路径。例如,`TEST_ENVOY_BINARY_PATH=$(which envoy) go test -v ./tests/integration -run TestPluginXX`。注意 Envoy 二进制和 Go 插件编译出来的 so 文件需要是兼容的: + +* 编译的平台要一致 +* glibc 版本要兼容 +* 使用的 Envoy 接口版本要一致(见 [HTNN 的 Envoy 多版本支持](./dataplane_support.md)) + +默认 binary mode 下,测试框架会花 1 秒等待 Envoy 启动。这个时间可以通过环境变量 `TEST_ENVOY_WAIT_BINARY_TO_START_TIME` 来修改。例如,`TEST_ENVOY_BINARY_MODE_WAIT_TIME=2s TEST_ENVOY_BINARY_PATH=$(which envoy) go test -v ./tests/integration -run TestFilterManagerEncode`。 + ## 端口使用 测试框架将占用 host 上的下述端口: