mirror of https://github.com/go-gost/gost.git
e2e: add node liveness probe tests
TCP probe + FailFilter: dead node pre-marked, all requests succeed. LowestLatency strategy: two probed nodes, selector picks fastest.master
parent
1c6fadcb1e
commit
bef00dffef
|
|
@ -0,0 +1,90 @@
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/testcontainers/testcontainers-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProbeSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
ctx context.Context
|
||||||
|
echoC testcontainers.Container
|
||||||
|
echoIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProbeSuite) SetupSuite() {
|
||||||
|
s.ctx = context.Background()
|
||||||
|
|
||||||
|
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoC = echoC
|
||||||
|
|
||||||
|
echoIP, err := echoC.ContainerIP(s.ctx)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.echoIP = echoIP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProbeSuite) TearDownSuite() {
|
||||||
|
if s.echoC != nil {
|
||||||
|
s.echoC.Terminate(s.ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProbeSuite) proxyRequest(gostC testcontainers.Container, port string) (int, string) {
|
||||||
|
cmd := []string{
|
||||||
|
"curl", "-s",
|
||||||
|
"-x", fmt.Sprintf("http://127.0.0.1:%s", port),
|
||||||
|
fmt.Sprintf("http://%s:5678", s.echoIP),
|
||||||
|
}
|
||||||
|
code, out, err := gostC.Exec(s.ctx, cmd)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
body, err := io.ReadAll(out)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
return code, string(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestTCPProbeFailover verifies that the TCP probe detects a dead node and
|
||||||
|
// marks it before any real traffic, so the FailFilter excludes it. With the
|
||||||
|
// dead node pre-marked, every request succeeds immediately.
|
||||||
|
func (s *ProbeSuite) TestTCPProbeFailover() {
|
||||||
|
gostC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, "testdata/probe/tcp.yaml", "8080/tcp")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer gostC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
// The probe fires immediately at startup, so the dead node is already
|
||||||
|
// marked by the time we send requests.
|
||||||
|
for range 10 {
|
||||||
|
code, body := s.proxyRequest(gostC, "8080")
|
||||||
|
s.Require().Equal(0, code, "every request must succeed; dead node pre-marked by probe")
|
||||||
|
s.Require().Contains(body, "hello-gost")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLowestLatencyProbe verifies that the lowestlatency strategy works with
|
||||||
|
// probed nodes. Both nodes are live; the strategy selects the one with the
|
||||||
|
// lowest measured latency.
|
||||||
|
func (s *ProbeSuite) TestLowestLatencyProbe() {
|
||||||
|
gostC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, "testdata/probe/lowestlatency.yaml", "8080/tcp")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer gostC.Terminate(s.ctx)
|
||||||
|
|
||||||
|
// Give the initial probe a moment to fire so both nodes show healthy.
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
for range 10 {
|
||||||
|
code, body := s.proxyRequest(gostC, "8080")
|
||||||
|
s.Require().Equal(0, code, "all requests must succeed with lowestlatency strategy")
|
||||||
|
s.Require().Contains(body, "hello-gost")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProbeSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ProbeSuite))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
services:
|
||||||
|
- name: proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: my-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
# Two live relays — both forward to the echo server.
|
||||||
|
- name: relay-a
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
- name: relay-b
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: my-chain
|
||||||
|
hops:
|
||||||
|
- name: hop-1
|
||||||
|
selector:
|
||||||
|
strategy: lowestlatency
|
||||||
|
maxFails: 1
|
||||||
|
failTimeout: 10s
|
||||||
|
nodes:
|
||||||
|
- name: node-a
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
probe:
|
||||||
|
type: tcp
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
interval: 5s
|
||||||
|
- name: node-b
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
probe:
|
||||||
|
type: tcp
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
interval: 5s
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
services:
|
||||||
|
- name: proxy
|
||||||
|
addr: :8080
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
chain: my-chain
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
# Live relay: forwards to the echo server.
|
||||||
|
- name: relay
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
handler:
|
||||||
|
type: http
|
||||||
|
listener:
|
||||||
|
type: tcp
|
||||||
|
|
||||||
|
chains:
|
||||||
|
- name: my-chain
|
||||||
|
hops:
|
||||||
|
- name: hop-1
|
||||||
|
selector:
|
||||||
|
strategy: round
|
||||||
|
maxFails: 1
|
||||||
|
failTimeout: 10s
|
||||||
|
nodes:
|
||||||
|
- name: node-live
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
probe:
|
||||||
|
type: tcp
|
||||||
|
addr: 127.0.0.1:18081
|
||||||
|
interval: 5s
|
||||||
|
# dead node: probed, marked as failed by the probe goroutine,
|
||||||
|
# then excluded by FailFilter before any real traffic tries it.
|
||||||
|
- name: node-dead
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
connector:
|
||||||
|
type: http
|
||||||
|
probe:
|
||||||
|
type: tcp
|
||||||
|
addr: 127.0.0.1:18082
|
||||||
|
interval: 5s
|
||||||
Loading…
Reference in New Issue