diff options
author | Matus Fabian <matfabia@cisco.com> | 2024-08-14 12:38:20 +0200 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2024-08-22 03:25:19 +0000 |
commit | 8792e5c5c5e8e4f6c514ff81c97a7fb31890d657 (patch) | |
tree | ebb33de76afe10d080969ee6fc9d543935e1564a /extras/hs-test/infra | |
parent | 58cb6ba81833a79a877448aa60d7d68604ad6c4e (diff) |
hs-test: proxy testing improvement
- new container topologies and suites for VPP proxy and Envoy proxy
- removed build docker image since it can't be used with CI cache
builder, container builders are designed to be stateless, they
only preserve build-cache, but not images
Type: test
Change-Id: I93e4d079780d18d6aa3b5ce807adc4707b6f2d9b
Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'extras/hs-test/infra')
-rw-r--r-- | extras/hs-test/infra/suite_envoy_proxy.go | 195 | ||||
-rw-r--r-- | extras/hs-test/infra/suite_vpp_proxy.go | 179 | ||||
-rw-r--r-- | extras/hs-test/infra/utils.go | 57 |
3 files changed, 431 insertions, 0 deletions
diff --git a/extras/hs-test/infra/suite_envoy_proxy.go b/extras/hs-test/infra/suite_envoy_proxy.go new file mode 100644 index 00000000000..e1bfae293a2 --- /dev/null +++ b/extras/hs-test/infra/suite_envoy_proxy.go @@ -0,0 +1,195 @@ +// Suite for Envoy proxy testing +// +// The topology consists of 4 containers: curl (client), VPP (session layer), Envoy (proxy), nginx (target HTTP server). +// VPP has 2 tap interfaces configured, one for client network and second for server/target network. + +package hst + +import ( + "fmt" + . "github.com/onsi/ginkgo/v2" + "reflect" + "runtime" + "strings" +) + +const ( + VppContainerName = "vpp" + EnvoyProxyContainerName = "envoy-vcl" +) + +type EnvoyProxySuite struct { + HstSuite + nginxPort uint16 + proxyPort uint16 +} + +var envoyProxyTests = map[string][]func(s *EnvoyProxySuite){} +var envoyProxySoloTests = map[string][]func(s *EnvoyProxySuite){} + +func RegisterEnvoyProxyTests(tests ...func(s *EnvoyProxySuite)) { + envoyProxyTests[getTestFilename()] = tests +} + +func RegisterEnvoyProxySoloTests(tests ...func(s *EnvoyProxySuite)) { + envoyProxySoloTests[getTestFilename()] = tests +} + +func (s *EnvoyProxySuite) SetupSuite() { + s.HstSuite.SetupSuite() + s.LoadNetworkTopology("2taps") + s.LoadContainerTopology("envoyProxy") +} + +func (s *EnvoyProxySuite) SetupTest() { + s.HstSuite.SetupTest() + + // VPP + var sessionConfig Stanza + sessionConfig. + NewStanza("session"). + Append("enable"). + Append("use-app-socket-api"). + Append("evt_qs_memfd_seg"). + Append("event-queue-length 100000") + + vppContainer := s.GetContainerByName(VppContainerName) + vpp, err := vppContainer.newVppInstance(vppContainer.AllocatedCpus, sessionConfig) + s.AssertNotNil(vpp, fmt.Sprint(err)) + s.AssertNil(vpp.Start()) + clientInterface := s.GetInterfaceByName(ClientTapInterfaceName) + s.AssertNil(vpp.createTap(clientInterface, 1)) + serverInterface := s.GetInterfaceByName(ServerTapInterfaceName) + s.AssertNil(vpp.createTap(serverInterface, 2)) + vppContainer.Exec("chmod 777 -R %s", vppContainer.GetContainerWorkDir()) + + // nginx HTTP server + nginxContainer := s.GetTransientContainerByName(NginxServerContainerName) + s.AssertNil(nginxContainer.Create()) + s.nginxPort = 80 + nginxSettings := struct { + LogPrefix string + Address string + Port uint16 + }{ + LogPrefix: nginxContainer.Name, + Address: serverInterface.Ip4AddressString(), + Port: s.nginxPort, + } + nginxContainer.CreateConfig( + "/nginx.conf", + "./resources/nginx/nginx_server.conf", + nginxSettings, + ) + s.AssertNil(nginxContainer.Start()) + + // Envoy + envoyContainer := s.GetContainerByName(EnvoyProxyContainerName) + s.AssertNil(envoyContainer.Create()) + s.proxyPort = 8080 + envoySettings := struct { + LogPrefix string + ServerAddress string + ServerPort uint16 + ProxyPort uint16 + }{ + LogPrefix: envoyContainer.Name, + ServerAddress: serverInterface.Ip4AddressString(), + ServerPort: s.nginxPort, + ProxyPort: s.proxyPort, + } + envoyContainer.CreateConfig( + "/etc/envoy/envoy.yaml", + "resources/envoy/proxy.yaml", + envoySettings, + ) + s.AssertNil(envoyContainer.Start()) + + // Add Ipv4 ARP entry for nginx HTTP server, otherwise first request fail (HTTP error 503) + arp := fmt.Sprintf("set ip neighbor %s %s %s", + serverInterface.Peer.Name(), + serverInterface.Ip4AddressString(), + serverInterface.HwAddress) + vppContainer.VppInstance.Vppctl(arp) +} + +func (s *EnvoyProxySuite) TearDownTest() { + if CurrentSpecReport().Failed() { + s.CollectNginxLogs(NginxServerContainerName) + s.CollectEnvoyLogs(EnvoyProxyContainerName) + } + s.HstSuite.TearDownTest() +} + +func (s *EnvoyProxySuite) ProxyPort() uint16 { + return s.proxyPort +} + +func (s *EnvoyProxySuite) ProxyAddr() string { + return s.GetInterfaceByName(ClientTapInterfaceName).Peer.Ip4AddressString() +} + +func (s *EnvoyProxySuite) CurlDownloadResource(uri string) { + args := fmt.Sprintf("--insecure --noproxy '*' --remote-name --output-dir /tmp %s", uri) + _, log := s.RunCurlContainer(args) + s.AssertNotContains(log, "Recv failure") + s.AssertContains(log, "HTTP/1.1 200") +} + +var _ = Describe("EnvoyProxySuite", Ordered, ContinueOnFailure, func() { + var s EnvoyProxySuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for filename, tests := range envoyProxyTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.Log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(SuiteTimeout)) + } + } +}) + +var _ = Describe("EnvoyProxySuiteSolo", Ordered, ContinueOnFailure, func() { + var s EnvoyProxySuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for filename, tests := range envoyProxySoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.Log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(SuiteTimeout)) + } + } +}) diff --git a/extras/hs-test/infra/suite_vpp_proxy.go b/extras/hs-test/infra/suite_vpp_proxy.go new file mode 100644 index 00000000000..bfe7de7f54b --- /dev/null +++ b/extras/hs-test/infra/suite_vpp_proxy.go @@ -0,0 +1,179 @@ +// Suite for VPP proxy testing +// +// The topology consists of 3 containers: curl (client), VPP (proxy), nginx (target HTTP server). +// VPP has 2 tap interfaces configured, one for client network and second for server/target network. + +package hst + +import ( + "fmt" + . "github.com/onsi/ginkgo/v2" + "reflect" + "runtime" + "strings" +) + +// These correspond to names used in yaml config +const ( + ClientTapInterfaceName = "hstcln" + ServerTapInterfaceName = "hstsrv" +) + +type VppProxySuite struct { + HstSuite + nginxPort uint16 +} + +var vppProxyTests = map[string][]func(s *VppProxySuite){} +var vppProxySoloTests = map[string][]func(s *VppProxySuite){} + +func RegisterVppProxyTests(tests ...func(s *VppProxySuite)) { + vppProxyTests[getTestFilename()] = tests +} + +func RegisterVppProxySoloTests(tests ...func(s *VppProxySuite)) { + vppProxySoloTests[getTestFilename()] = tests +} + +func (s *VppProxySuite) SetupSuite() { + s.HstSuite.SetupSuite() + s.LoadNetworkTopology("2taps") + s.LoadContainerTopology("vppProxy") +} + +func (s *VppProxySuite) SetupTest() { + s.HstSuite.SetupTest() + + // VPP HTTP connect-proxy + vppContainer := s.GetContainerByName(VppProxyContainerName) + vpp, err := vppContainer.newVppInstance(vppContainer.AllocatedCpus) + s.AssertNotNil(vpp, fmt.Sprint(err)) + s.AssertNil(vpp.Start()) + clientInterface := s.GetInterfaceByName(ClientTapInterfaceName) + s.AssertNil(vpp.createTap(clientInterface, 1)) + serverInterface := s.GetInterfaceByName(ServerTapInterfaceName) + s.AssertNil(vpp.createTap(serverInterface, 2)) + + // nginx HTTP server + nginxContainer := s.GetTransientContainerByName(NginxServerContainerName) + s.AssertNil(nginxContainer.Create()) + s.nginxPort = 80 + nginxSettings := struct { + LogPrefix string + Address string + Port uint16 + }{ + LogPrefix: nginxContainer.Name, + Address: serverInterface.Ip4AddressString(), + Port: s.nginxPort, + } + nginxContainer.CreateConfig( + "/nginx.conf", + "./resources/nginx/nginx_server.conf", + nginxSettings, + ) + s.AssertNil(nginxContainer.Start()) +} + +func (s *VppProxySuite) TearDownTest() { + if CurrentSpecReport().Failed() { + s.CollectNginxLogs(NginxServerContainerName) + } + s.HstSuite.TearDownTest() +} + +func (s *VppProxySuite) NginxPort() uint16 { + return s.nginxPort +} + +func (s *VppProxySuite) NginxAddr() string { + return s.GetInterfaceByName(ServerTapInterfaceName).Ip4AddressString() +} + +func (s *VppProxySuite) VppProxyAddr() string { + return s.GetInterfaceByName(ClientTapInterfaceName).Peer.Ip4AddressString() +} + +func (s *VppProxySuite) CurlRequest(targetUri string) (string, string) { + args := fmt.Sprintf("--insecure --noproxy '*' %s", targetUri) + body, log := s.RunCurlContainer(args) + return body, log +} + +func (s *VppProxySuite) CurlRequestViaTunnel(targetUri string, proxyUri string) (string, string) { + args := fmt.Sprintf("--insecure -p -x %s %s", proxyUri, targetUri) + body, log := s.RunCurlContainer(args) + return body, log +} + +func (s *VppProxySuite) CurlDownloadResource(uri string) { + args := fmt.Sprintf("--insecure --noproxy '*' --remote-name --output-dir /tmp %s", uri) + _, log := s.RunCurlContainer(args) + s.AssertNotContains(log, "Recv failure") + s.AssertContains(log, "HTTP/1.1 200") +} + +func (s *VppProxySuite) CurlDownloadResourceViaTunnel(uri string, proxyUri string) { + args := fmt.Sprintf("--insecure -p -x %s --remote-name --output-dir /tmp %s", proxyUri, uri) + _, log := s.RunCurlContainer(args) + s.AssertNotContains(log, "Recv failure") + s.AssertContains(log, "HTTP/1.1 200") +} + +var _ = Describe("VppProxySuite", Ordered, ContinueOnFailure, func() { + var s VppProxySuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for filename, tests := range vppProxyTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, func(ctx SpecContext) { + s.Log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(SuiteTimeout)) + } + } +}) + +var _ = Describe("VppProxySuiteSolo", Ordered, ContinueOnFailure, func() { + var s VppProxySuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterAll(func() { + s.TearDownSuite() + }) + AfterEach(func() { + s.TearDownTest() + }) + + for filename, tests := range vppProxySoloTests { + for _, test := range tests { + test := test + pc := reflect.ValueOf(test).Pointer() + funcValue := runtime.FuncForPC(pc) + testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2] + It(testName, Label("SOLO"), func(ctx SpecContext) { + s.Log(testName + ": BEGIN") + test(&s) + }, SpecTimeout(SuiteTimeout)) + } + } +}) diff --git a/extras/hs-test/infra/utils.go b/extras/hs-test/infra/utils.go index 05b7b365487..3df83f32508 100644 --- a/extras/hs-test/infra/utils.go +++ b/extras/hs-test/infra/utils.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httputil" "os" + "os/exec" "strings" "time" ) @@ -126,3 +127,59 @@ func TcpSendReceive(address, data string) (string, error) { } return string(reply), nil } + +/* +RunCurlContainer execute curl command with given args. +Container with name "curl" must be available. +Curl runs in verbose mode and progress meter switch off by default. +*/ +func (s *HstSuite) RunCurlContainer(args string) (string, string) { + curlCont := s.GetContainerByName("curl") + cmd := fmt.Sprintf("curl -v -s %s", args) + s.Log(cmd) + curlCont.ExtraRunningArgs = cmd + curlCont.Run() + stdout, stderr := curlCont.GetOutput() + s.Log(stderr) + s.Log(stdout) + return stdout, stderr +} + +/* +CollectNginxLogs save access and error logs to the test execution directory. +Nginx logging need to be set following way: + + - error_log <default-work-dir>/{{.LogPrefix}}-error.log; + - access_log <default-work-dir>/{{.LogPrefix}}-access.log; + +where LogPrefix is set to nginxContainer.Name +*/ +func (s *HstSuite) CollectNginxLogs(containerName string) { + nginxContainer := s.GetContainerByName(containerName) + targetDir := nginxContainer.getLogDirPath() + source := nginxContainer.GetHostWorkDir() + "/" + nginxContainer.Name + "-" + cmd := exec.Command("cp", "-t", targetDir, source+"error.log", source+"access.log") + s.Log(cmd.String()) + err := cmd.Run() + if err != nil { + s.Log(fmt.Sprint(err)) + } +} + +/* +CollectEnvoyLogs save access logs to the test execution directory. +Envoy access log path need to be set following way: +<default-work-dir>/{{.LogPrefix}}-access.log +where LogPrefix is set to envoyContainer.Name +*/ +func (s *HstSuite) CollectEnvoyLogs(containerName string) { + envoyContainer := s.GetContainerByName(containerName) + targetDir := envoyContainer.getLogDirPath() + source := envoyContainer.GetHostWorkDir() + "/" + envoyContainer.Name + "-" + cmd := exec.Command("cp", "-t", targetDir, source+"access.log") + s.Log(cmd.String()) + err := cmd.Run() + if err != nil { + s.Log(fmt.Sprint(err)) + } +} |