From 31b9071cc17a50748def677e218ab9cd0e6e2055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=B3=BD=E8=BD=A9?= Date: Tue, 7 Nov 2023 13:39:52 +0800 Subject: [PATCH] init ci (#14) Signed-off-by: spacewander --- .github/workflows/lint.yml | 30 +++++++++++ .github/workflows/test.yml | 50 +++++++++++++++++++ Makefile | 16 ++++-- .../plugins/control_plane/control_plane.go | 12 ++++- .../plugins/data_plane/data_plane.go | 17 +++++-- tests/integration/plugins/demo_test.go | 2 +- .../integration/plugins/filtermanager_test.go | 10 ++-- 7 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..72313965 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,30 @@ +name: lint + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + + - name: format go + run: | + make fmt-go + if !git diff --exit-code; then + echo "Files are not well formatted" + exit 1 + fi + + - name: lint go + run: make lint-go + + - name: lint spell + run: make lint-spell diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..95a7a5f1 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,50 @@ +name: test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + IN_CI: true + +jobs: + unit-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + # Note: the Go toolchain here is only used to provide go env + # To update the go version, please specify the TEST_IMAGE in Makefile + go-version: '1.20' + + - name: Build + run: make build-so + + - name: Test + run: make unit-test + + integration-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + + - name: Build + run: make build-test-so + + - name: Test + run: make integration-test + - name: Upload logs + uses: actions/upload-artifact@v3 + if: failure() + with: + # upload artifact can be found in https://github.com/mosn/moe/actions/runs/$id + name: ci-logs + path: ./test-envoy diff --git a/Makefile b/Makefile index 80248be5..8a378938 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ SHELL = /bin/bash OS = $(shell uname) +IN_CI ?= TARGET_SO = libgolang.so PROJECT_NAME = mosn.io/moe @@ -26,6 +27,15 @@ GO_TARGETS = $(patsubst %.proto,%.pb.go,$(PROTO_FILES)) TEST_OPTION ?= -gcflags="all=-N -l" -v -race +MOUNT_GOMOD_CACHE = -v $(shell go env GOPATH):/go +ifeq ($(IN_CI), true) + # Mount go mod cache in the CI environment will cause 'Permission denied' error + # when accessing files on host in later phase because the mounted directory will + # have files which is created by the root user in Docker. + # Run as low privilege user in the Docker doesn't + # work because we also need root to create /.cache in the Docker. + MOUNT_GOMOD_CACHE = +endif .PHONY: gen-proto gen-proto: build-dev-tools $(GO_TARGETS) @@ -42,7 +52,7 @@ gen-proto: build-dev-tools $(GO_TARGETS) # The conclusion is, we prefer easier to write test to easier to run test. .PHONY: unit-test unit-test: - docker run --rm -v $(shell go env GOPATH):/go -v $(PWD):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${TEST_IMAGE} make unit-test-local + docker run --rm ${MOUNT_GOMOD_CACHE} -v $(PWD):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} ${TEST_IMAGE} make unit-test-local .PHONY: unit-test-local unit-test-local: @@ -60,13 +70,13 @@ build-test-so-local: # So here we disable the error via git configuration when running inside Docker. .PHONY: build-test-so build-test-so: - docker run --rm -v $(shell go env GOPATH):/go -v $(PWD):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} \ + docker run --rm ${MOUNT_GOMOD_CACHE} -v $(PWD):/go/src/${PROJECT_NAME} -w /go/src/${PROJECT_NAME} \ -e GOPROXY \ ${BUILD_IMAGE} \ bash -c "git config --global --add safe.directory '*' && make build-test-so-local" .PHONY: integration-test -integration-test: build-test-so +integration-test: if ! docker images ${PROXY_IMAGE} | grep envoyproxy/envoy > /dev/null; then \ docker pull ${PROXY_IMAGE}; \ fi diff --git a/tests/integration/plugins/control_plane/control_plane.go b/tests/integration/plugins/control_plane/control_plane.go index 8428b9ea..5ca670f8 100644 --- a/tests/integration/plugins/control_plane/control_plane.go +++ b/tests/integration/plugins/control_plane/control_plane.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net" + "runtime" "time" xds "github.com/cncf/xds/go/xds/type/v3" @@ -56,7 +57,16 @@ func NewControlPlane() *ControlPlane { } func (cp *ControlPlane) Start() { - lis, _ := net.Listen("tcp", "127.0.0.1:9999") + host := "127.0.0.1" + if runtime.GOOS == "linux" { + // We need to use 0.0.0.0 on Linux so the data plane in the Docker + // can connect to it. + host = "0.0.0.0" + // Use 0.0.0.0 on Mac will be prompted by some security policies so we + // only use it on Linux. + } + + lis, _ := net.Listen("tcp", host+":9999") if err := cp.grpcServer.Serve(lis); err != nil { logger.Error(err, "failed to start control plane") } diff --git a/tests/integration/plugins/data_plane/data_plane.go b/tests/integration/plugins/data_plane/data_plane.go index 6384e044..e1764687 100644 --- a/tests/integration/plugins/data_plane/data_plane.go +++ b/tests/integration/plugins/data_plane/data_plane.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "testing" "time" @@ -33,7 +34,9 @@ type Option struct { func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { if opt == nil { - opt = &Option{} + opt = &Option{ + LogLevel: "debug", + } } dp := &DataPlane{ @@ -54,6 +57,14 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { 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" + } + // This is the envoyproxy/envoy:contrib-debug-dev fetched in 2023-10-27 // Use docker inspect --format='{{index .RepoDigests 0}}' envoyproxy/envoy:contrib-debug-dev // to get the sha256 ID @@ -66,12 +77,12 @@ func StartDataPlane(t *testing.T, opt *Option) (*DataPlane, error) { "/tests/integration/plugins/data_plane/envoy.yaml:/etc/envoy.yaml -v " + projectRoot + "/tests/integration/plugins/libgolang.so:/etc/libgolang.so" + - " -p 10000:10000 -p 9998:9998 " + + " -p 10000:10000 -p 9998:9998 " + hostAddr + " " + image + " " + envoyCmd logger.Info("run cmd", "cmdline", cmdline) - cmds := strings.Split(cmdline, " ") + cmds := strings.Fields(cmdline) cmd := exec.Command(cmds[0], cmds[1:]...) stdout, err := os.Create(filepath.Join(dir, "stdout")) diff --git a/tests/integration/plugins/demo_test.go b/tests/integration/plugins/demo_test.go index c3aaec84..60cea74e 100644 --- a/tests/integration/plugins/demo_test.go +++ b/tests/integration/plugins/demo_test.go @@ -12,7 +12,7 @@ import ( ) func TestDemo(t *testing.T) { - dp, err := data_plane.StartDataPlane(t, nil) + dp, err := data_plane.StartDataPlane(t, &data_plane.Option{}) if err != nil { t.Fatalf("failed to start data plane: %v", err) return diff --git a/tests/integration/plugins/filtermanager_test.go b/tests/integration/plugins/filtermanager_test.go index ffeef876..bb3efc76 100644 --- a/tests/integration/plugins/filtermanager_test.go +++ b/tests/integration/plugins/filtermanager_test.go @@ -20,9 +20,7 @@ func assertBody(t *testing.T, exp string, resp *http.Response) { } func TestFilterManagerDecode(t *testing.T) { - dp, err := data_plane.StartDataPlane(t, &data_plane.Option{ - LogLevel: "debug", - }) + dp, err := data_plane.StartDataPlane(t, &data_plane.Option{}) if err != nil { t.Fatalf("failed to start data plane: %v", err) return @@ -228,7 +226,7 @@ func TestFilterManagerDecode(t *testing.T) { rd, wt := io.Pipe() go func() { for i := 0; i < 2; i++ { - time.Sleep(10 * time.Millisecond) + time.Sleep(20 * time.Millisecond) _, err := wt.Write([]byte(strconv.Itoa(i))) assert.Nil(t, err) } @@ -248,9 +246,7 @@ func assertBodyHas(t *testing.T, exp string, resp *http.Response) { } func TestFilterManagerEncode(t *testing.T) { - dp, err := data_plane.StartDataPlane(t, &data_plane.Option{ - LogLevel: "debug", - }) + dp, err := data_plane.StartDataPlane(t, &data_plane.Option{}) if err != nil { t.Fatalf("failed to start data plane: %v", err) return