mirror of
https://github.com/fatedier/frp.git
synced 2026-04-28 20:19:10 +08:00
test/e2e: add compatibility test suite for wire protocol version negotiation (#5297)
This commit is contained in:
236
test/e2e/compatibility/compatibility_test.go
Normal file
236
test/e2e/compatibility/compatibility_test.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
"github.com/fatedier/frp/pkg/util/log"
|
||||
"github.com/fatedier/frp/test/e2e/framework"
|
||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||
"github.com/fatedier/frp/test/e2e/pkg/process"
|
||||
)
|
||||
|
||||
type compatTestContext struct {
|
||||
CurrentFRPSPath string
|
||||
CurrentFRPCPath string
|
||||
BaselineFRPSPath string
|
||||
BaselineFRPCPath string
|
||||
BaselineVersion string
|
||||
LogLevel string
|
||||
Debug bool
|
||||
RunCurrentCurrent bool
|
||||
}
|
||||
|
||||
var compatCtx compatTestContext
|
||||
|
||||
func registerFlags(flags *flag.FlagSet) {
|
||||
flags.StringVar(&compatCtx.CurrentFRPSPath, "current-frps-path", "../../bin/frps", "The current frps binary to use.")
|
||||
flags.StringVar(&compatCtx.CurrentFRPCPath, "current-frpc-path", "../../bin/frpc", "The current frpc binary to use.")
|
||||
flags.StringVar(&compatCtx.BaselineFRPSPath, "baseline-frps-path", "", "The baseline frps binary to use.")
|
||||
flags.StringVar(&compatCtx.BaselineFRPCPath, "baseline-frpc-path", "", "The baseline frpc binary to use.")
|
||||
flags.StringVar(&compatCtx.BaselineVersion, "baseline-version", "custom", "The baseline version label for reporting.")
|
||||
flags.StringVar(&compatCtx.LogLevel, "log-level", "debug", "Log level.")
|
||||
flags.BoolVar(&compatCtx.Debug, "debug", false, "Enable debug mode to print detailed info.")
|
||||
flags.BoolVar(&compatCtx.RunCurrentCurrent, "run-current-current", true, "Run current frps/current frpc sanity checks.")
|
||||
}
|
||||
|
||||
func validateCompatContext(t *compatTestContext) error {
|
||||
paths := map[string]string{
|
||||
"current-frps-path": t.CurrentFRPSPath,
|
||||
"current-frpc-path": t.CurrentFRPCPath,
|
||||
"baseline-frps-path": t.BaselineFRPSPath,
|
||||
"baseline-frpc-path": t.BaselineFRPCPath,
|
||||
}
|
||||
for name, path := range paths {
|
||||
if path == "" {
|
||||
return fmt.Errorf("%s can't be empty", name)
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return fmt.Errorf("load %s error: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
registerFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
if err := validateCompatContext(&compatCtx); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
framework.TestContext.Debug = compatCtx.Debug
|
||||
framework.TestContext.LogLevel = compatCtx.LogLevel
|
||||
log.InitLogger("console", compatCtx.LogLevel, 0, true)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestCompatibilityE2E(t *testing.T) {
|
||||
gomega.RegisterFailHandler(framework.Fail)
|
||||
|
||||
suiteConfig, reporterConfig := ginkgo.GinkgoConfiguration()
|
||||
suiteConfig.EmitSpecProgress = true
|
||||
suiteConfig.RandomizeAllSpecs = true
|
||||
ginkgo.RunSpecs(t, "frp compatibility e2e suite", suiteConfig, reporterConfig)
|
||||
}
|
||||
|
||||
var _ = ginkgo.Describe("[Compatibility: WireProtocol]", func() {
|
||||
f := framework.NewDefaultFramework()
|
||||
|
||||
ginkgo.It("current frps and current frpc support v1 and v2", func() {
|
||||
if !compatCtx.RunCurrentCurrent {
|
||||
ginkgo.Skip("current/current sanity checks already ran")
|
||||
}
|
||||
|
||||
webPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
webServer.port = %d
|
||||
`, webPort)
|
||||
|
||||
v1PortName := port.GenName("CompatCurrentV1")
|
||||
v1ClientConf := tcpClientConfig("compat-current-v1", v1PortName, `
|
||||
clientID = "compat-current-v1"
|
||||
transport.wireProtocol = "v1"
|
||||
`)
|
||||
|
||||
v2PortName := port.GenName("CompatCurrentV2")
|
||||
v2ClientConf := tcpClientConfig("compat-current-v2", v2PortName, `
|
||||
clientID = "compat-current-v2"
|
||||
transport.wireProtocol = "v2"
|
||||
`)
|
||||
|
||||
f.RunProcessesWithBinaries(
|
||||
compatCtx.CurrentFRPSPath,
|
||||
compatCtx.CurrentFRPCPath,
|
||||
serverConf,
|
||||
[]string{v1ClientConf, v2ClientConf},
|
||||
)
|
||||
|
||||
framework.NewRequestExpect(f).PortName(v1PortName).Ensure()
|
||||
framework.NewRequestExpect(f).PortName(v2PortName).Ensure()
|
||||
expectClientWireProtocol(webPort, "compat-current-v1", "v1")
|
||||
expectClientWireProtocol(webPort, "compat-current-v2", "v2")
|
||||
})
|
||||
|
||||
ginkgo.It("current frps accepts baseline frpc using v1", func() {
|
||||
webPort := f.AllocPort()
|
||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||
webServer.port = %d
|
||||
`, webPort)
|
||||
|
||||
portName := port.GenName("CompatBaselineFRPC")
|
||||
clientConf := tcpClientConfig("tcp", portName, "")
|
||||
|
||||
f.RunProcessesWithBinaries(
|
||||
compatCtx.CurrentFRPSPath,
|
||||
compatCtx.BaselineFRPCPath,
|
||||
serverConf,
|
||||
[]string{clientConf},
|
||||
)
|
||||
|
||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||
expectSingleClientWireProtocol(webPort, "v1")
|
||||
})
|
||||
|
||||
ginkgo.It("baseline frps accepts current frpc defaulting to v1", func() {
|
||||
portName := port.GenName("CompatBaselineFRPS")
|
||||
clientConf := tcpClientConfig("tcp", portName, "")
|
||||
|
||||
f.RunProcessesWithBinaries(
|
||||
compatCtx.BaselineFRPSPath,
|
||||
compatCtx.CurrentFRPCPath,
|
||||
consts.DefaultServerConfig,
|
||||
[]string{clientConf},
|
||||
)
|
||||
|
||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||
})
|
||||
|
||||
ginkgo.It("baseline frps rejects current frpc forced to v2", func() {
|
||||
portName := port.GenName("CompatBaselineFRPSForcedV2")
|
||||
clientConf := tcpClientConfig("tcp", portName, `
|
||||
transport.wireProtocol = "v2"
|
||||
`)
|
||||
|
||||
_, clientProcesses := f.RunProcessesWithBinaries(
|
||||
compatCtx.BaselineFRPSPath,
|
||||
compatCtx.CurrentFRPCPath,
|
||||
consts.DefaultServerConfig,
|
||||
[]string{clientConf},
|
||||
)
|
||||
expectProcessExit(clientProcesses[0], 5*time.Second)
|
||||
framework.NewRequestExpect(f).PortName(portName).ExpectError(true).Ensure()
|
||||
})
|
||||
})
|
||||
|
||||
func tcpClientConfig(proxyName string, remotePortName string, extra string) string {
|
||||
return fmt.Sprintf(`
|
||||
serverAddr = "127.0.0.1"
|
||||
serverPort = {{ .%s }}
|
||||
loginFailExit = true
|
||||
log.level = "trace"
|
||||
%s
|
||||
|
||||
[[proxies]]
|
||||
name = "%s"
|
||||
type = "tcp"
|
||||
localPort = {{ .%s }}
|
||||
remotePort = {{ .%s }}
|
||||
`, consts.PortServerName, extra, proxyName, framework.TCPEchoServerPort, remotePortName)
|
||||
}
|
||||
|
||||
func expectProcessExit(p *process.Process, timeout time.Duration) {
|
||||
select {
|
||||
case <-p.Done():
|
||||
case <-time.After(timeout):
|
||||
framework.Failf("process did not exit within %s; output:\n%s", timeout, p.Output())
|
||||
}
|
||||
}
|
||||
|
||||
type wireClientInfo struct {
|
||||
ClientID string `json:"clientID"`
|
||||
WireProtocol string `json:"wireProtocol"`
|
||||
}
|
||||
|
||||
func expectSingleClientWireProtocol(webPort int, wireProtocol string) {
|
||||
clients := getWireClientInfos(webPort)
|
||||
framework.ExpectEqual(len(clients), 1)
|
||||
framework.ExpectEqual(clients[0].WireProtocol, wireProtocol)
|
||||
}
|
||||
|
||||
func expectClientWireProtocol(webPort int, clientID string, wireProtocol string) {
|
||||
for _, client := range getWireClientInfos(webPort) {
|
||||
if client.ClientID == clientID {
|
||||
framework.ExpectEqual(client.WireProtocol, wireProtocol)
|
||||
return
|
||||
}
|
||||
}
|
||||
framework.Failf("client %q not found in /api/clients response", clientID)
|
||||
}
|
||||
|
||||
func getWireClientInfos(webPort int) []wireClientInfo {
|
||||
client := http.Client{Timeout: consts.DefaultTimeout}
|
||||
resp, err := client.Get(fmt.Sprintf("http://127.0.0.1:%d/api/clients", webPort))
|
||||
framework.ExpectNoError(err)
|
||||
defer resp.Body.Close()
|
||||
framework.ExpectEqual(resp.StatusCode, http.StatusOK)
|
||||
|
||||
content, err := io.ReadAll(resp.Body)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
var clients []wireClientInfo
|
||||
framework.ExpectNoError(json.Unmarshal(content, &clients))
|
||||
return clients
|
||||
}
|
||||
Reference in New Issue
Block a user