Сервер Go gRPC делает интеграционные тесты неудачными

Недавно я снова начал играть с go, и моя задача на данный момент - реализовать сервис (который будет работать на k8s), который должен действовать как сервер gRPC.

В соответствии с требованиями, на данный момент, чтобы удовлетворить readinessProbe на k8s, мне нужно реализовать конечную точку проверки работоспособности для моей службы, и это должно быть протестировано: я сделал следующее:

func main() {
    server := startHTTPServer()
    defer server.Close()

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)

    <-c
    log.Println("Stopped")
}

func startHTTPServer() *http.Server {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    })

    server := &http.Server{
        Addr: net.JoinHostPort("", strconv.Itoa(livelinessPort)),
    }

    go server.ListenAndServe()

    return server
}

и я тестировал таким образом, следуя этой статье:

func TestMain(m *testing.M) {
    flag.Parse()

    wg := setup()

    result := m.Run()

    shutdown(wg)

    os.Exit(result)
}

func shutdown(wg *sync.WaitGroup) {
    syscall.Kill(syscall.Getpid(), syscall.SIGINT)
    wg.Wait()
}

func setup() *sync.WaitGroup{
    os.Setenv("APP_ENV", EnvTest)

    wg := &sync.WaitGroup{}
    startMainProcess(wg)
    return wg
}

func startMainProcess(wg *sync.WaitGroup) {
    go func() {
        wg.Add(1)
        defer wg.Done()
        main()
    }()
}

func TestK8SHealth(t *testing.T) {
    res, err := http.Get("http://:8080/")
    if err != nil {
        t.Errorf("Unexpected API error: %s", err)
        return
    }

    defer res.Body.Close()

    if res.StatusCode != http.StatusOK {
        t.Errorf("Unexpected status code: %d", res.StatusCode)
        return
    }
}

и пока все хорошо:

$ go test
PASS
2018/08/13 09:23:16 Stopped
ok      github.com/...        0.015s

Проблема заключается в том, что я пытаюсь добавить сервер gRPC в основное приложение, следуя примерам gPRC go.. Я добавил это в свой основной файл

func main() {
    [...]

    startChecksServiceServer()

    [...]
}

func startChecksServiceServer() *grpc.Server {
    flag.Parse()
    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
    if err != nil {
        log.Fatalf("Starting gPRC server error: failed to listen port %d: %v", serverPort, err)
    }

    grpcServer := grpc.NewServer()

    pb.RegisterChecksServiceServer(grpcServer, &checksServiceServer{})

    grpcServer.Serve(lis)
    log.Println("Started grpc server")
    return grpcServer
}

но теперь, когда я запускаю тест, он терпит неудачу после того, как сам тест был запущен, когда отправляется сигнал прерывания.

$  go test 
PASS
signal: interrupt
FAIL    github.com/...        0.016s

Я провел несколько тестов, и это не зависит от фактического теста (поскольку он проходит с тестовой функцией, которая просто return, и она работает при запуске набора тестов с использованием goLand:

GOROOT=/usr/local/go #gosetup
GOPATH=/Users/marco/Documents/projects/go #gosetup
/usr/local/go/bin/go test -c -i -o /private/var/folders/jh/znhv9f090yb7y390dckb23s00000gn/T/[...] #gosetup
/usr/local/go/bin/go tool test2json -t /private/var/folders/jh/znhv9f090yb7y390dckb23s00000gn/T/[...] -test.v -test.run
^TestK8SHealth$ #gosetup
=== RUN   TestK8SHealth
--- PASS: TestK8SHealth (0.00s)
PASS

Process finished with exit code 1

--- ИЗМЕНИТЬ

Я обнаружил, что проблема в слушателе. Имея в основной функции:

func main() {
    net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)

    <-c
    log.Println("Stopped")
}

и тест с одним только возвратом (с той же настройкой, что и предыдущий) приведет к сбою теста:

$ go test -v
=== RUN   Test2
--- PASS: Test2 (0.00s)
PASS
signal: interrupt
FAIL    github.com/[...]        0.014s

person Marco Vignati    schedule 13.08.2018    source источник


Ответы (1)


grpcServer.Serve(lis) - это блокирующий вызов. Как и в случае с HTTP-сервером go server.ListenAndServe(), вам нужно запустить его в горутине. Не могли бы вы попробовать, пройдет ли тест go grpcServer.Serve(lis)?

Я не уверен в вашем новом EDIT, который указывает на то, что проблема связана с слушателем. Вы можете проверить, возвращается ли net.Listen("tcp", fmt.Sprintf(":%d", serverPort)) или блокируется.

person lyuxuan    schedule 15.08.2018