From ca5f1e615644cdbeda4a3deb480f732eb50ec1cd Mon Sep 17 00:00:00 2001 From: Adrian Villin Date: Mon, 12 May 2025 15:16:18 +0200 Subject: [PATCH] hs-test: further separated KinD from HST suites - KinD suite was also improved and cleaned up a bit Type: test Change-Id: Iee1adb69f935b69b79d70a541fa40f5f5d73298d Signed-off-by: Adrian Villin --- extras/hs-test/README.rst | 21 ++- extras/hs-test/framework_test.go | 2 +- extras/hs-test/infra/common/suite_common.go | 191 +++++++++++++++++++ extras/hs-test/infra/common/utils_common.go | 126 +++++++++++++ extras/hs-test/infra/container.go | 17 +- extras/hs-test/infra/cpu.go | 1 + extras/hs-test/infra/hst_suite.go | 207 ++------------------- extras/hs-test/infra/infra_kind/deployment.go | 118 ------------ extras/hs-test/infra/infra_kind/suite_kind.go | 200 -------------------- extras/hs-test/infra/kind/deployment.go | 104 +++++++++++ extras/hs-test/infra/kind/pod.go | 50 +++++ extras/hs-test/infra/kind/suite_kind.go | 141 ++++++++++++++ extras/hs-test/infra/{infra_kind => kind}/utils.go | 28 +-- extras/hs-test/infra/suite_cpu_pinning.go | 13 +- extras/hs-test/infra/suite_envoy_proxy.go | 13 +- extras/hs-test/infra/suite_h2.go | 16 +- extras/hs-test/infra/suite_iperf_linux.go | 9 +- extras/hs-test/infra/suite_ldp.go | 13 +- extras/hs-test/infra/suite_nginx_proxy.go | 13 +- extras/hs-test/infra/suite_no_topo.go | 13 +- extras/hs-test/infra/suite_no_topo6.go | 13 +- extras/hs-test/infra/suite_veth.go | 9 +- extras/hs-test/infra/suite_veth6.go | 9 +- extras/hs-test/infra/suite_vpp_proxy.go | 13 +- extras/hs-test/infra/suite_vpp_udp_proxy.go | 13 +- extras/hs-test/infra/utils.go | 99 ---------- extras/hs-test/infra/vppinstance.go | 3 +- extras/hs-test/kind_test.go | 30 +-- extras/hs-test/ldp_test.go | 1 + 29 files changed, 748 insertions(+), 738 deletions(-) create mode 100644 extras/hs-test/infra/common/suite_common.go create mode 100644 extras/hs-test/infra/common/utils_common.go delete mode 100644 extras/hs-test/infra/infra_kind/deployment.go delete mode 100644 extras/hs-test/infra/infra_kind/suite_kind.go create mode 100644 extras/hs-test/infra/kind/deployment.go create mode 100644 extras/hs-test/infra/kind/pod.go create mode 100644 extras/hs-test/infra/kind/suite_kind.go rename extras/hs-test/infra/{infra_kind => kind}/utils.go (65%) diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst index 8deebe39832..ca9f1f9e55e 100644 --- a/extras/hs-test/README.rst +++ b/extras/hs-test/README.rst @@ -22,7 +22,7 @@ Anatomy of a test case * Install hs-test dependencies with ``make install-deps`` * `Install Go `_, it has to be in path of both the running user (follow instructions on Go installation page) and root (run ``sudo visudo`` and edit ``secure_path`` line, run ``sudo go version`` to verify) * Root privileges are required to run tests as it uses Linux ``ip`` command for configuring topology -* Tests use *hs-test*'s own docker image, they are rebuild automatically when needed, you can run ``make build[-debug]`` to do so or use ``FORCE_BUILD=true`` make parameter +* Tests use *hs-test*'s own docker image, they are rebuilt automatically when needed, you can run ``make build[-debug]`` to do so or use ``FORCE_BUILD=true`` make parameter **Action flow when running a test case**: @@ -50,12 +50,11 @@ For adding a new suite, please see `Modifying the framework`_ below. #. Declare method whose name ends with ``Test`` and specifies its parameter as a pointer to the suite's struct (defined in ``infra/suite_*.go``) #. Implement test behaviour inside the test method. This typically includes the following: - #. Import ``. "fd.io/hs-test/infra"`` - #. Retrieve a running container in which to run some action. Method ``GetContainerByName`` - from ``HstSuite`` struct serves this purpose + #. Import ``. "fd.io/hs-test/infra"`` and ``. "fd.io/hs-test/infra/infra_common"`` + #. Retrieve a running container in which to run some action. Each suite has a struct called ``Containers`` #. Interact with VPP through the ``VppInstance`` struct embedded in container. It provides ``Vppctl`` method to access debug CLI #. Run arbitrary commands inside the containers with ``Exec`` method - #. Run other external tool with one of the preexisting functions in the ``infra/utils.go`` file. + #. Run other external tool with one of the preexisting functions in the ``infra/utils.go`` or ``infra/infra_common/utils_common.go`` file. For example, use ``wget`` with ``StartWget`` function #. Use ``exechelper`` or just plain ``exec`` packages to run whatever else #. Verify results of your tests using ``Assert`` methods provided by the test suite. @@ -103,7 +102,7 @@ when running in parallel. Filtering test cases -------------------- -The framework allows us to filter test cases in a few different ways, using ``make test TEST=``: +The framework allows us to filter test cases in a few different ways, using ``make test TEST=xyz SKIP=xyz``: * Suite name * File name @@ -111,6 +110,8 @@ The framework allows us to filter test cases in a few different ways, using ``ma * All of the above as long as they are ordered properly, e.g. ``make test TEST=VethsSuite.http_test.go.HeaderServerTest`` * Multiple tests/suites: ``make test TEST=HttpClient,LdpSuite`` +All of the above also applies to ``SKIP`` + **Names are case sensitive!** Names don't have to be complete, as long as they are last: @@ -227,10 +228,10 @@ Modifying the framework s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range myTests { @@ -361,7 +362,7 @@ Utility methods **Packet Capture** It is possible to use VPP pcap trace to capture received and sent packets. -You just need to add ``EnablePcapTrace`` to ``SetupTest`` method in test suite and ``CollectPcapTrace`` to ``TearDownTest``. +You just need to add ``EnablePcapTrace`` to ``SetupTest`` method in test suite and ``CollectPcapTrace`` to ``TeardownTest``. This way pcap trace is enabled on all interfaces and to capture maximum 10000 packets. Your pcap file will be located in the test execution directory. @@ -369,7 +370,7 @@ Your pcap file will be located in the test execution directory. ``clib_warning`` is a handy way to add debugging output, but in some cases it's not appropriate for per-packet use in data plane code. In this case VPP event logger is better option, for example you can enable it for TCP or session layer in build time. -To collect traces when test ends you just need to add ``CollectEventLogs`` method to ``TearDownTest`` in the test suite. +To collect traces when test ends you just need to add ``CollectEventLogs`` method to ``TeardownTest`` in the test suite. Your event logger file will be located in the test execution directory. To view events you can use :ref:`G2 graphical event viewer ` or ``convert_evt`` tool, located in ``src/scripts/host-stack/``, which convert event logs to human readable text. diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go index 66c5e50bd84..962d6d5fcd6 100644 --- a/extras/hs-test/framework_test.go +++ b/extras/hs-test/framework_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - . "fd.io/hs-test/infra" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/extras/hs-test/infra/common/suite_common.go b/extras/hs-test/infra/common/suite_common.go new file mode 100644 index 00000000000..72173c6261a --- /dev/null +++ b/extras/hs-test/infra/common/suite_common.go @@ -0,0 +1,191 @@ +package hst_common + +import ( + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "strconv" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var IsCoverage = flag.Bool("coverage", false, "use coverage run config") +var IsPersistent = flag.Bool("persist", false, "persists topology config") +var IsVerbose = flag.Bool("verbose", false, "verbose test output") +var SudoUser = flag.String("sudo_user", "root", "what user ran hs-test with sudo") +var ParallelTotal = flag.Lookup("ginkgo.parallel.total") +var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp") +var DryRun = flag.Bool("dryrun", false, "set up containers but don't run tests") +var Timeout = flag.Int("timeout", 5, "test timeout override (in minutes)") +var NumaAwareCpuAlloc bool +var TestTimeout time.Duration +var RunningInCi bool + +const ( + LogDir string = "/tmp/hs-test/" + VolumeDir string = "/volumes" +) + +type HstCommon struct { + Ppid string + ProcessIndex string + Logger *log.Logger + LogFile *os.File +} + +func (s *HstCommon) Skip(args string) { + Skip(args) +} + +func (s *HstCommon) SetupTest() { + TestCounterFunc() + s.Log("[* TEST SETUP]") +} + +func (s *HstCommon) SetupSuite() { + s.CreateLogger() + s.Log("[* SUITE SETUP]") + s.Ppid = fmt.Sprint(os.Getppid()) + // remove last number so we have space to prepend a process index (interfaces have a char limit) + s.Ppid = s.Ppid[:len(s.Ppid)-1] + s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess()) +} + +func (s *HstCommon) TeardownTest() { + if *IsPersistent || *DryRun { + return + } + s.Log("[* TEST TEARDOWN]") +} + +func (s *HstCommon) TeardownSuite() { + if *IsPersistent || *DryRun { + return + } + s.Log("[* SUITE TEARDOWN]") +} + +func (s *HstCommon) GetCurrentSuiteName() string { + return CurrentSpecReport().ContainerHierarchyTexts[0] +} + +func (s *HstCommon) CreateLogger() { + suiteName := s.GetCurrentSuiteName() + var err error + s.LogFile, err = os.Create("summary/" + suiteName + ".log") + if err != nil { + Fail("Unable to create log file.") + } + s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags) +} + +// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter +// to keep console tidy +func (s *HstCommon) Log(log any, arg ...any) { + var logStr string + if len(arg) == 0 { + logStr = fmt.Sprint(log) + } else { + logStr = fmt.Sprintf(fmt.Sprint(log), arg...) + } + logs := strings.Split(logStr, "\n") + + for _, line := range logs { + s.Logger.Println(line) + } + if *IsVerbose { + GinkgoWriter.Println(logStr) + } +} + +func (s *HstCommon) AssertNil(object interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...) +} + +func (s *HstCommon) AssertNotNil(object interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...) +} + +func (s *HstCommon) AssertEqual(expected, actual interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...) +} + +func (s *HstCommon) AssertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...) +} + +func (s *HstCommon) AssertContains(testString, contains interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).To(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...) +} + +func (s *HstCommon) AssertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).ToNot(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...) +} + +func (s *HstCommon) AssertEmpty(object interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...) +} + +func (s *HstCommon) AssertNotEmpty(object interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...) +} + +func (s *HstCommon) AssertMatchError(actual, expected error, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...) +} + +func (s *HstCommon) AssertGreaterThan(actual, expected interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...) +} + +func (s *HstCommon) AssertEqualWithinThreshold(actual, expected, threshold interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).Should(BeNumerically("~", expected, threshold), msgAndArgs...) +} + +func (s *HstCommon) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...interface{}) { + ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...) +} + +func (s *HstCommon) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...interface{}) { + ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...) +} + +func (s *HstCommon) AssertHttpHeaderWithValue(resp *http.Response, key string, value interface{}, msgAndArgs ...interface{}) { + ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...) +} + +func (s *HstCommon) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...interface{}) { + ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...) +} + +func (s *HstCommon) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...interface{}) { + ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...) +} + +func (s *HstCommon) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...interface{}) { + ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...) +} + +func (s *HstCommon) AssertChannelClosed(timeout time.Duration, channel chan error) { + EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed()) +} + +// Pass the parsed result struct and the minimum amount of data transferred in MB. +// Won't do anything when testing a coverage build. +func (s *HstCommon) AssertIperfMinTransfer(result IPerfResult, minTransferred int) { + if *IsCoverage { + s.Log("Coverage build; not asserting") + return + } + if result.Start.Details.Protocol == "TCP" { + s.AssertGreaterThan(result.End.TcpReceived.MBytes, minTransferred) + } else { + s.AssertGreaterThan(result.End.Udp.MBytes, minTransferred) + } +} diff --git a/extras/hs-test/infra/common/utils_common.go b/extras/hs-test/infra/common/utils_common.go new file mode 100644 index 00000000000..5b3d7181748 --- /dev/null +++ b/extras/hs-test/infra/common/utils_common.go @@ -0,0 +1,126 @@ +package hst_common + +import ( + "encoding/json" + "fmt" + "path/filepath" + "runtime" + "strings" + "time" +) + +func GetTestFilename() string { + _, filename, _, _ := runtime.Caller(2) + return filepath.Base(filename) +} + +var testCounter uint16 +var startTime time.Time = time.Now() + +func TestCounterFunc() { + if ParallelTotal.Value.String() != "1" { + return + } + testCounter++ + fmt.Printf("Test counter: %d\n"+ + "Time elapsed: %.2fs\n", + testCounter, time.Since(startTime).Seconds()) +} + +type IPerfResult struct { + Start struct { + Timestamp struct { + Time string `json:"time"` + } `json:"timestamp"` + Connected []struct { + Socket int `json:"socket"` + LocalHost string `json:"local_host"` + LocalPort int `json:"local_port"` + RemoteHost string `json:"remote_host"` + RemotePort int `json:"remote_port"` + } `json:"connected"` + Version string `json:"version"` + Details struct { + Protocol string `json:"protocol"` + } `json:"test_start"` + } `json:"start"` + End struct { + TcpSent *struct { + MbitsPerSecond float64 `json:"bits_per_second"` + MBytes float64 `json:"bytes"` + } `json:"sum_sent,omitempty"` + TcpReceived *struct { + MbitsPerSecond float64 `json:"bits_per_second"` + MBytes float64 `json:"bytes"` + } `json:"sum_received,omitempty"` + Udp *struct { + MbitsPerSecond float64 `json:"bits_per_second"` + JitterMs float64 `json:"jitter_ms,omitempty"` + LostPackets int `json:"lost_packets,omitempty"` + Packets int `json:"packets,omitempty"` + LostPercent float64 `json:"lost_percent,omitempty"` + MBytes float64 `json:"bytes"` + } `json:"sum,omitempty"` + } `json:"end"` +} + +func (s *HstCommon) ParseJsonIperfOutput(jsonResult []byte) IPerfResult { + var result IPerfResult + // remove iperf warning line if present + if strings.Contains(string(jsonResult), "warning") { + index := strings.Index(string(jsonResult), "\n") + jsonResult = jsonResult[index+1:] + } + + err := json.Unmarshal(jsonResult, &result) + s.AssertNil(err) + + if result.Start.Details.Protocol == "TCP" { + result.End.TcpSent.MbitsPerSecond = result.End.TcpSent.MbitsPerSecond / 1000000 + result.End.TcpSent.MBytes = result.End.TcpSent.MBytes / 1000000 + result.End.TcpReceived.MbitsPerSecond = result.End.TcpReceived.MbitsPerSecond / 1000000 + result.End.TcpReceived.MBytes = result.End.TcpReceived.MBytes / 1000000 + } else { + result.End.Udp.MBytes = result.End.Udp.MBytes / 1000000 + result.End.Udp.MbitsPerSecond = result.End.Udp.MbitsPerSecond / 1000000 + } + + return result +} + +func (s *HstCommon) LogJsonIperfOutput(result IPerfResult) { + s.Log("\n*******************************************\n"+ + "%s\n"+ + "[%s] %s:%d connected to %s:%d\n"+ + "Started: %s\n", + result.Start.Version, + result.Start.Details.Protocol, + result.Start.Connected[0].LocalHost, result.Start.Connected[0].LocalPort, + result.Start.Connected[0].RemoteHost, result.Start.Connected[0].RemotePort, + result.Start.Timestamp.Time) + + if result.Start.Details.Protocol == "TCP" { + s.Log("Transfer (sent): %.2f MBytes\n"+ + "Bitrate (sent): %.2f Mbits/sec\n"+ + "Transfer (received): %.2f MBytes\n"+ + "Bitrate (received): %.2f Mbits/sec", + result.End.TcpSent.MBytes, + result.End.TcpSent.MbitsPerSecond, + result.End.TcpReceived.MBytes, + result.End.TcpReceived.MbitsPerSecond) + } else { + s.Log("Transfer: %.2f MBytes\n"+ + "Bitrate: %.2f Mbits/sec\n"+ + "Jitter: %.3f ms\n"+ + "Packets: %d\n"+ + "Packets lost: %d\n"+ + "Percent lost: %.2f%%", + result.End.Udp.MBytes, + result.End.Udp.MbitsPerSecond, + result.End.Udp.JitterMs, + result.End.Udp.Packets, + result.End.Udp.LostPackets, + result.End.Udp.LostPercent) + } + s.Log("*******************************************\n") +} diff --git a/extras/hs-test/infra/container.go b/extras/hs-test/infra/container.go index 341c92597cf..5bb8040005e 100644 --- a/extras/hs-test/infra/container.go +++ b/extras/hs-test/infra/container.go @@ -13,20 +13,15 @@ import ( "text/template" "time" - "github.com/docker/go-units" - + . "fd.io/hs-test/infra/common" "github.com/cilium/cilium/pkg/sysctl" containerTypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/pkg/stdcopy" + "github.com/docker/go-units" "github.com/edwarnicke/exechelper" - . "github.com/onsi/ginkgo/v2" -) - -const ( - LogDir string = "/tmp/hs-test/" - volumeDir string = "/volumes" + "github.com/onsi/ginkgo/v2" ) var ( @@ -93,7 +88,7 @@ func newContainer(suite *HstSuite, yamlInput ContainerConfig) (*Container, error } if _, ok := yamlInput["volumes"]; ok { - workingVolumeDir := LogDir + suite.GetCurrentTestName() + volumeDir + workingVolumeDir := LogDir + suite.GetCurrentTestName() + VolumeDir workDirReplacer := strings.NewReplacer("$HST_DIR", workDir) volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) for _, volu := range yamlInput["volumes"].([]interface{}) { @@ -458,7 +453,7 @@ func (c *Container) ExecServer(useEnvVars bool, command string, arguments ...any envVars = "" } containerExecCommand := fmt.Sprintf("docker exec -d %s %s %s", envVars, c.Name, serverCommand) - GinkgoHelper() + ginkgo.GinkgoHelper() c.Suite.Log(containerExecCommand) c.Suite.AssertNil(exechelper.Run(containerExecCommand)) } @@ -472,7 +467,7 @@ func (c *Container) Exec(useEnvVars bool, command string, arguments ...any) (str envVars = "" } containerExecCommand := fmt.Sprintf("docker exec %s %s %s", envVars, c.Name, serverCommand) - GinkgoHelper() + ginkgo.GinkgoHelper() c.Suite.Log(containerExecCommand) byteOutput, err := exechelper.CombinedOutput(containerExecCommand) return string(byteOutput), err diff --git a/extras/hs-test/infra/cpu.go b/extras/hs-test/infra/cpu.go index 4afc96bcee4..d6ae14e7f46 100644 --- a/extras/hs-test/infra/cpu.go +++ b/extras/hs-test/infra/cpu.go @@ -8,6 +8,7 @@ import ( "os/exec" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) diff --git a/extras/hs-test/infra/hst_suite.go b/extras/hs-test/infra/hst_suite.go index 49b1aa9958e..03eaff58d89 100644 --- a/extras/hs-test/infra/hst_suite.go +++ b/extras/hs-test/infra/hst_suite.go @@ -4,53 +4,39 @@ import ( "bufio" "flag" "fmt" - "io" - "log" "net/http" "net/http/httputil" "os" "os/exec" - "path/filepath" "runtime" "slices" "strconv" "strings" "time" - "github.com/edwarnicke/exechelper" - + . "fd.io/hs-test/infra/common" containerTypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" - "github.com/onsi/gomega/gmeasure" - "gopkg.in/yaml.v3" - + "github.com/edwarnicke/exechelper" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/onsi/gomega/gmeasure" + "gopkg.in/yaml.v3" ) const ( DEFAULT_NETWORK_NUM int = 1 ) -var IsPersistent = flag.Bool("persist", false, "persists topology config") -var IsVerbose = flag.Bool("verbose", false, "verbose test output") -var IsCoverage = flag.Bool("coverage", false, "use coverage run config") var IsUnconfiguring = flag.Bool("unconfigure", false, "remove topology") -var IsVppDebug = flag.Bool("debug", false, "attach gdb to vpp") var NConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp") var VppSourceFileDir = flag.String("vppsrc", "", "vpp source file directory") var IsDebugBuild = flag.Bool("debug_build", false, "some paths are different with debug build") var UseCpu0 = flag.Bool("cpu0", false, "use cpu0") var IsLeakCheck = flag.Bool("leak_check", false, "run leak-check tests") -var ParallelTotal = flag.Lookup("ginkgo.parallel.total") -var DryRun = flag.Bool("dryrun", false, "set up containers but don't run tests") -var SudoUser = flag.String("sudo_user", "root", "what user ran hs-test with sudo") -var Timeout = flag.Int("timeout", 5, "test timeout override (in minutes)") -var NumaAwareCpuAlloc bool -var TestTimeout time.Duration -var RunningInCi bool type HstSuite struct { + HstCommon AllContainers map[string]*Container StartedContainers []*Container Volumes []string @@ -62,10 +48,6 @@ type HstSuite struct { CpuAllocator *CpuAllocatorT CpuContexts []*CpuContext CpuCount int - Ppid string - ProcessIndex string - Logger *log.Logger - LogFile *os.File Docker *client.Client CoverageRun bool } @@ -110,19 +92,6 @@ type StringerStruct struct { Label string } -var testCounter uint16 -var startTime time.Time = time.Now() - -func testCounterFunc() { - if ParallelTotal.Value.String() != "1" { - return - } - testCounter++ - fmt.Printf("Test counter: %d\n"+ - "Time elapsed: %.2fs\n", - testCounter, time.Since(startTime).Seconds()) -} - // ColorableString for ReportEntry to use func (s StringerStruct) ColorableString() string { return fmt.Sprintf("{{red}}%s{{/}}", s.Label) @@ -133,11 +102,6 @@ func (s StringerStruct) String() string { return s.Label } -func GetTestFilename() string { - _, filename, _, _ := runtime.Caller(2) - return filepath.Base(filename) -} - func (s *HstSuite) getLogDirPath() string { testId := s.GetTestId() testName := s.GetCurrentTestName() @@ -158,32 +122,15 @@ func (s *HstSuite) newDockerClient() { s.Log("docker client created") } -func (s *HstSuite) SetupKindSuite() { - s.CreateLogger() - s.Log("[* SUITE SETUP]") - RegisterFailHandler(func(message string, callerSkip ...int) { - s.HstFail() - Fail(message, callerSkip...) - }) - s.Ppid = fmt.Sprint(os.Getppid()) - // remove last number so we have space to prepend a process index (interfaces have a char limit) - s.Ppid = s.Ppid[:len(s.Ppid)-1] - s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess()) -} - func (s *HstSuite) SetupSuite() { - s.CreateLogger() - s.Log("[* SUITE SETUP]") - s.newDockerClient() RegisterFailHandler(func(message string, callerSkip ...int) { s.HstFail() Fail(message, callerSkip...) }) + s.HstCommon.SetupSuite() + s.newDockerClient() + var err error - s.Ppid = fmt.Sprint(os.Getppid()) - // remove last number so we have space to prepend a process index (interfaces have a char limit) - s.Ppid = s.Ppid[:len(s.Ppid)-1] - s.ProcessIndex = fmt.Sprint(GinkgoParallelProcess()) s.CpuAllocator, err = CpuAllocator() if err != nil { Fail("failed to init cpu allocator: " + fmt.Sprint(err)) @@ -232,21 +179,15 @@ func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) { s.CpuContexts = append(s.CpuContexts, cpuCtx) } -func (s *HstSuite) TearDownSuite() { +func (s *HstSuite) TeardownSuite() { + s.HstCommon.TeardownSuite() defer s.LogFile.Close() defer s.Docker.Close() - if *IsPersistent || *DryRun { - return - } - s.Log("[* SUITE TEARDOWN]") s.UnconfigureNetworkTopology() } -func (s *HstSuite) TearDownTest() { - s.Log("[* TEST TEARDOWN]") - if *IsPersistent || *DryRun { - return - } +func (s *HstSuite) TeardownTest() { + s.HstCommon.TeardownTest() coreDump := s.WaitForCoreDump() s.ResetContainers() @@ -276,8 +217,7 @@ func (s *HstSuite) SkipIfNotCoverage() { } func (s *HstSuite) SetupTest() { - testCounterFunc() - s.Log("[* TEST SETUP]") + s.HstCommon.SetupTest() s.StartedContainers = s.StartedContainers[:0] s.SkipIfUnconfiguring() s.SetupContainers() @@ -340,125 +280,6 @@ func (s *HstSuite) HstFail() { } } -func (s *HstSuite) AssertNil(object interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, object).To(BeNil(), msgAndArgs...) -} - -func (s *HstSuite) AssertNotNil(object interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, object).ToNot(BeNil(), msgAndArgs...) -} - -func (s *HstSuite) AssertEqual(expected, actual interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).To(Equal(expected), msgAndArgs...) -} - -func (s *HstSuite) AssertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).ToNot(Equal(expected), msgAndArgs...) -} - -func (s *HstSuite) AssertContains(testString, contains interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).To(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...) -} - -func (s *HstSuite) AssertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, strings.ToLower(fmt.Sprint(testString))).ToNot(ContainSubstring(strings.ToLower(fmt.Sprint(contains))), msgAndArgs...) -} - -func (s *HstSuite) AssertEmpty(object interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, object).To(BeEmpty(), msgAndArgs...) -} - -func (s *HstSuite) AssertNotEmpty(object interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, object).ToNot(BeEmpty(), msgAndArgs...) -} - -func (s *HstSuite) AssertMatchError(actual, expected error, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).To(MatchError(expected), msgAndArgs...) -} - -func (s *HstSuite) AssertGreaterThan(actual, expected interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).Should(BeNumerically(">=", expected), msgAndArgs...) -} - -func (s *HstSuite) AssertEqualWithinThreshold(actual, expected, threshold interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).Should(BeNumerically("~", expected, threshold), msgAndArgs...) -} - -func (s *HstSuite) AssertTimeEqualWithinThreshold(actual, expected time.Time, threshold time.Duration, msgAndArgs ...interface{}) { - ExpectWithOffset(2, actual).Should(BeTemporally("~", expected, threshold), msgAndArgs...) -} - -func (s *HstSuite) AssertHttpStatus(resp *http.Response, expectedStatus int, msgAndArgs ...interface{}) { - ExpectWithOffset(2, resp).To(HaveHTTPStatus(expectedStatus), msgAndArgs...) -} - -func (s *HstSuite) AssertHttpHeaderWithValue(resp *http.Response, key string, value interface{}, msgAndArgs ...interface{}) { - ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue(key, value), msgAndArgs...) -} - -func (s *HstSuite) AssertHttpHeaderNotPresent(resp *http.Response, key string, msgAndArgs ...interface{}) { - ExpectWithOffset(2, resp.Header.Get(key)).To(BeEmpty(), msgAndArgs...) -} - -func (s *HstSuite) AssertHttpContentLength(resp *http.Response, expectedContentLen int64, msgAndArgs ...interface{}) { - ExpectWithOffset(2, resp).To(HaveHTTPHeaderWithValue("Content-Length", strconv.FormatInt(expectedContentLen, 10)), msgAndArgs...) -} - -func (s *HstSuite) AssertHttpBody(resp *http.Response, expectedBody string, msgAndArgs ...interface{}) { - ExpectWithOffset(2, resp).To(HaveHTTPBody(expectedBody), msgAndArgs...) -} - -func (s *HstSuite) AssertChannelClosed(timeout time.Duration, channel chan error) { - EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed()) -} - -// Pass the parsed result struct and the minimum amount of data transferred in MB. -// Won't do anything when testing a coverage build. -func (s *HstSuite) AssertIperfMinTransfer(result IPerfResult, minTransferred int) { - if *IsCoverage { - s.Log("Coverage build; not asserting") - return - } - if result.Start.Details.Protocol == "TCP" { - s.AssertGreaterThan(result.End.TcpReceived.MBytes, minTransferred) - } else { - s.AssertGreaterThan(result.End.Udp.MBytes, minTransferred) - } -} - -func (s *HstSuite) CreateLogger() { - suiteName := s.GetCurrentSuiteName() - var err error - s.LogFile, err = os.Create("summary/" + suiteName + ".log") - if err != nil { - Fail("Unable to create log file.") - } - s.Logger = log.New(io.Writer(s.LogFile), "", log.LstdFlags) -} - -// Logs to files by default, logs to stdout when VERBOSE=true with GinkgoWriter -// to keep console tidy -func (s *HstSuite) Log(log any, arg ...any) { - var logStr string - if len(arg) == 0 { - logStr = fmt.Sprint(log) - } else { - logStr = fmt.Sprintf(fmt.Sprint(log), arg...) - } - logs := strings.Split(logStr, "\n") - - for _, line := range logs { - s.Logger.Println(line) - } - if *IsVerbose { - GinkgoWriter.Println(logStr) - } -} - -func (s *HstSuite) Skip(args string) { - Skip(args) -} - func (s *HstSuite) SkipIfMultiWorker(args ...any) { if *NConfiguredCpus > 1 { s.Skip("test case not supported with multiple vpp workers") @@ -602,7 +423,7 @@ func (s *HstSuite) LoadContainerTopology(topologyName string) { for _, elem := range yamlTopo.Volumes { volumeMap := elem["volume"].(VolumeConfig) hostDir := volumeMap["host-dir"].(string) - workingVolumeDir := LogDir + s.GetCurrentTestName() + volumeDir + workingVolumeDir := LogDir + s.GetCurrentTestName() + VolumeDir volDirReplacer := strings.NewReplacer("$HST_VOLUME_DIR", workingVolumeDir) hostDir = volDirReplacer.Replace(hostDir) s.Volumes = append(s.Volumes, hostDir) diff --git a/extras/hs-test/infra/infra_kind/deployment.go b/extras/hs-test/infra/infra_kind/deployment.go deleted file mode 100644 index baa665b80a7..00000000000 --- a/extras/hs-test/infra/infra_kind/deployment.go +++ /dev/null @@ -1,118 +0,0 @@ -package hst - -import ( - "context" - "fmt" - "time" - - . "github.com/onsi/ginkgo/v2" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func (s *KindSuite) CreateNamespace(name string) { - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: s.Namespace, - }, - } - - // Create the namespace in the cluster - _, err := s.ClientSet.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) - s.AssertNil(err) - s.Log("Namespace '%s' created", s.Namespace) -} - -func (s *KindSuite) DeployServerClient(imageNameServer string, imageNameClient string, serverPod string, clientPod string) { - var err error - var counter uint8 - var serverDetails *corev1.Pod - s.CurrentlyRunning = append(s.CurrentlyRunning, serverPod, clientPod) - - server := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: s.Namespace, - Name: serverPod, - Labels: map[string]string{ - "app": serverPod, - }, - Annotations: map[string]string{ - "cni.projectcalico.org/vppVcl": "enable", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "server", - Image: imageNameServer, - SecurityContext: &corev1.SecurityContext{ - Privileged: boolPtr(true), - }, - Command: []string{"tail", "-f", "/dev/null"}, - ImagePullPolicy: corev1.PullIfNotPresent, - Ports: []corev1.ContainerPort{ - { - ContainerPort: 5201, - }, - }, - }, - }, - NodeName: "kind-worker", - }, - } - - // Create the Pod - _, err = s.ClientSet.CoreV1().Pods(s.Namespace).Create(context.TODO(), server, metav1.CreateOptions{}) - s.AssertNil(err) - s.Log("Pod '%s' created", serverPod) - - // Get IP - s.Log("Obtaining IP from '%s'", server.Name) - for s.ServerIp == "" { - serverDetails, err = s.ClientSet.CoreV1().Pods(s.Namespace).Get(context.TODO(), serverPod, metav1.GetOptions{}) - s.ServerIp = serverDetails.Status.PodIP - time.Sleep(time.Second * 1) - counter++ - if counter >= 10 { - Fail("Unable to get IP. Check if all pods are running. " + fmt.Sprint(err)) - } - } - - s.Log("IP: %s", s.ServerIp) - - client := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: s.Namespace, - Name: clientPod, - Annotations: map[string]string{ - "cni.projectcalico.org/vppVcl": "enable", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "client", - Image: imageNameClient, - ImagePullPolicy: corev1.PullIfNotPresent, - Command: []string{"tail", "-f", "/dev/null"}, - Ports: []corev1.ContainerPort{ - { - ContainerPort: 5201, - }, - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: boolPtr(true), - }, - }, - }, - NodeName: "kind-worker2", - }, - } - - _, err = s.ClientSet.CoreV1().Pods(s.Namespace).Create(context.TODO(), client, metav1.CreateOptions{}) - s.AssertNil(err) - s.Log("Pod '%s' created", clientPod) - - // let pods start properly - time.Sleep(time.Second * 5) -} diff --git a/extras/hs-test/infra/infra_kind/suite_kind.go b/extras/hs-test/infra/infra_kind/suite_kind.go deleted file mode 100644 index eac334ece76..00000000000 --- a/extras/hs-test/infra/infra_kind/suite_kind.go +++ /dev/null @@ -1,200 +0,0 @@ -package hst - -import ( - "context" - "errors" - "os" - "os/exec" - "reflect" - "runtime" - "strings" - "text/template" - "time" - - . "fd.io/hs-test/infra" - . "github.com/onsi/ginkgo/v2" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" -) - -type KindSuite struct { - HstSuite - ClientSet *kubernetes.Clientset - Config *rest.Config - ServerIp string - Namespace string - KubeconfigPath string - ImageNames - PodNames - ContainerNames -} - -type ImageNames struct { - HstVpp string - Nginx string - Ab string -} - -type PodNames struct { - ClientVpp string - ServerVpp string - Nginx string - Ab string - CurrentlyRunning []string -} - -type ContainerNames struct { - Server string - Client string -} - -var kindTests = map[string][]func(s *KindSuite){} - -func RegisterKindTests(tests ...func(s *KindSuite)) { - kindTests[GetTestFilename()] = tests -} - -func deletePod(clientset *kubernetes.Clientset, namespace, podName string) error { - return clientset.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{GracePeriodSeconds: int64Ptr(0)}) -} - -func deleteNamespace(clientset *kubernetes.Clientset, namespace string) error { - return clientset.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) -} - -func (s *KindSuite) loadDockerImages() { - s.Log("This may take a while. If you encounter problems, " + - "try loading docker images manually: 'kind load docker-image [image]'") - value := reflect.ValueOf(s.ImageNames) - reflType := reflect.TypeOf(s.ImageNames) - var cmd *exec.Cmd - var out []byte - var err error - - if reflType.Kind() == reflect.Struct { - for i := range value.NumField() { - if value.Field(i).Kind() == reflect.String { - fieldValue := value.Field(i).Interface().(string) - s.Log("loading docker image %s...", fieldValue) - cmd = exec.Command("kind", "load", "docker-image", fieldValue) - out, err = cmd.CombinedOutput() - s.Log(string(out)) - s.AssertNil(err, string(out)) - } - } - } else { - s.AssertNil(errors.New("not a struct")) - } -} - -func (s *KindSuite) SetupSuite() { - s.SetupKindSuite() - s.ImageNames.Ab = "hs-test/ab:latest" - s.ImageNames.Nginx = "hs-test/nginx-ldp:latest" - s.ImageNames.HstVpp = "hs-test/vpp:latest" - s.PodNames.ServerVpp = "server" + s.Ppid - s.PodNames.ClientVpp = "client" + s.Ppid - s.PodNames.Nginx = "nginx-ldp" + s.Ppid - s.PodNames.Ab = "ab" + s.Ppid - s.Namespace = "namespace" + s.Ppid - s.ContainerNames.Client = "client" - s.ContainerNames.Server = "server" - - s.loadDockerImages() - - var err error - if *SudoUser == "root" { - s.KubeconfigPath = "/.kube/config" - } else { - s.KubeconfigPath = "/home/" + *SudoUser + "/.kube/config" - } - - s.Config, err = clientcmd.BuildConfigFromFlags("", s.KubeconfigPath) - s.AssertNil(err) - - s.ClientSet, err = kubernetes.NewForConfig(s.Config) - s.AssertNil(err) - - s.CreateNamespace(s.Namespace) -} - -func (s *KindSuite) TeardownTest() { - if *IsPersistent { - return - } - s.Log("[TEST TEARDOWN]") - s.ServerIp = "" - if len(s.CurrentlyRunning) != 0 { - for _, pod := range s.CurrentlyRunning { - s.Log(" %s", pod) - deletePod(s.ClientSet, s.Namespace, pod) - } - } -} - -func (s *KindSuite) TeardownSuite() { - if *IsPersistent { - return - } - s.Log("[SUITE TEARDOWN]") - s.Log(" %s", s.Namespace) - s.AssertNil(deleteNamespace(s.ClientSet, s.Namespace)) -} - -func (s *KindSuite) CreateConfigFromTemplate(targetConfigName string, templateName string, values any) { - template := template.Must(template.ParseFiles(templateName)) - - f, err := os.CreateTemp(LogDir, "hst-config") - s.AssertNil(err, err) - defer os.Remove(f.Name()) - - err = template.Execute(f, values) - s.AssertNil(err, err) - - err = f.Close() - s.AssertNil(err, err) - - s.CopyToPod(s.PodNames.Nginx, s.Namespace, f.Name(), targetConfigName) -} - -func (s *KindSuite) CreateNginxConfig() { - values := struct { - Workers uint8 - }{ - Workers: 1, - } - s.CreateConfigFromTemplate( - "/nginx.conf", - "./resources/nginx/nginx.conf", - values, - ) -} - -var _ = Describe("KindSuite", Ordered, ContinueOnFailure, Label("Perf"), func() { - var s KindSuite - BeforeAll(func() { - s.SetupSuite() - }) - AfterEach(func() { - s.TeardownTest() - }) - AfterAll(func() { - s.TeardownSuite() - }) - - for filename, tests := range kindTests { - 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(time.Minute*15)) - } - } -}) diff --git a/extras/hs-test/infra/kind/deployment.go b/extras/hs-test/infra/kind/deployment.go new file mode 100644 index 00000000000..d28ffc8059d --- /dev/null +++ b/extras/hs-test/infra/kind/deployment.go @@ -0,0 +1,104 @@ +package hst_kind + +import ( + "context" + "fmt" + "os/exec" + "time" + + . "github.com/onsi/ginkgo/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (s *KindSuite) loadDockerImages() { + s.Log("This may take a while. If you encounter problems, " + + "try loading docker images manually: 'kind load docker-image [image]'") + + var cmd *exec.Cmd + var out []byte + var err error + for _, image := range s.images { + s.Log("loading docker image %s...", image) + cmd = exec.Command("kind", "load", "docker-image", image) + out, err = cmd.CombinedOutput() + s.Log(string(out)) + s.AssertNil(err, string(out)) + } +} + +func (s *KindSuite) createNamespace(name string) { + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + + // Create the namespace in the cluster + _, err := s.ClientSet.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) + s.AssertNil(err) + s.Log("Namespace '%s' created", name) +} + +func (s *KindSuite) deletePod(namespace string, podName string) error { + return s.ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{GracePeriodSeconds: int64Ptr(0)}) +} + +func (s *KindSuite) deleteNamespace(namespace string) error { + return s.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}) +} + +func (s *KindSuite) DeployPod(pod *Pod) { + s.CurrentlyRunning = append(s.CurrentlyRunning, pod.Name) + pod.CreatedPod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: s.Namespace, + Name: pod.Name, + Labels: map[string]string{ + "app": "HST", + }, + Annotations: map[string]string{ + "cni.projectcalico.org/vppVcl": "enable", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: pod.ContainerName, + Image: pod.Image, + SecurityContext: &corev1.SecurityContext{ + Privileged: boolPtr(true), + }, + Command: []string{"tail", "-f", "/dev/null"}, + ImagePullPolicy: corev1.PullIfNotPresent, + Ports: []corev1.ContainerPort{ + { + ContainerPort: 5201, + }, + }, + }, + }, + NodeName: pod.Worker, + }, + } + + // Create the Pod + _, err := s.ClientSet.CoreV1().Pods(s.Namespace).Create(context.TODO(), pod.CreatedPod, metav1.CreateOptions{}) + s.AssertNil(err) + s.Log("Pod '%s' created", pod.Name) + + // Get IP + s.Log("Obtaining IP from '%s'", pod.Name) + counter := 1 + for pod.IpAddress == "" { + pod.CreatedPod, err = s.ClientSet.CoreV1().Pods(s.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{}) + pod.IpAddress = pod.CreatedPod.Status.PodIP + time.Sleep(time.Second * 1) + counter++ + if counter >= 10 { + Fail("Unable to get IP. Check if all pods are running. " + fmt.Sprint(err)) + } + } + + s.Log("IP: %s", pod.IpAddress) +} diff --git a/extras/hs-test/infra/kind/pod.go b/extras/hs-test/infra/kind/pod.go new file mode 100644 index 00000000000..1b37047149b --- /dev/null +++ b/extras/hs-test/infra/kind/pod.go @@ -0,0 +1,50 @@ +package hst_kind + +import corev1 "k8s.io/api/core/v1" + +type Pod struct { + Name string + Image string + ContainerName string + Worker string + IpAddress string + CreatedPod *corev1.Pod +} + +// Sets pod names, image names, namespace name +func (s *KindSuite) initPods() { + wrk1 := "kind-worker" + wrk2 := "kind-worker2" + vppImg := "hs-test/vpp:latest" + nginxLdpImg := "hs-test/nginx-ldp:latest" + abImg := "hs-test/ab:latest" + clientCont := "client" + serverCont := "server" + + s.images = append(s.images, vppImg, nginxLdpImg, abImg) + s.Namespace = "namespace" + s.Ppid + + s.Pods.ClientGeneric = new(Pod) + s.Pods.ClientGeneric.Name = "client" + s.Ppid + s.Pods.ClientGeneric.Image = vppImg + s.Pods.ClientGeneric.ContainerName = clientCont + s.Pods.ClientGeneric.Worker = wrk1 + + s.Pods.ServerGeneric = new(Pod) + s.Pods.ServerGeneric.Name = "server" + s.Ppid + s.Pods.ServerGeneric.Image = vppImg + s.Pods.ServerGeneric.ContainerName = serverCont + s.Pods.ServerGeneric.Worker = wrk2 + + s.Pods.Ab = new(Pod) + s.Pods.Ab.Name = "ab" + s.Ppid + s.Pods.Ab.Image = abImg + s.Pods.Ab.ContainerName = clientCont + s.Pods.Ab.Worker = wrk1 + + s.Pods.Nginx = new(Pod) + s.Pods.Nginx.Name = "nginx-ldp" + s.Ppid + s.Pods.Nginx.Image = nginxLdpImg + s.Pods.Nginx.ContainerName = serverCont + s.Pods.Nginx.Worker = wrk2 +} diff --git a/extras/hs-test/infra/kind/suite_kind.go b/extras/hs-test/infra/kind/suite_kind.go new file mode 100644 index 00000000000..ffbd830ac46 --- /dev/null +++ b/extras/hs-test/infra/kind/suite_kind.go @@ -0,0 +1,141 @@ +package hst_kind + +import ( + "os" + "reflect" + "runtime" + "strings" + "text/template" + + . "fd.io/hs-test/infra/common" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +type KindSuite struct { + HstCommon + ClientSet *kubernetes.Clientset + Config *rest.Config + Namespace string + KubeconfigPath string + CurrentlyRunning []string + images []string + Pods struct { + ServerGeneric *Pod + ClientGeneric *Pod + Nginx *Pod + Ab *Pod + } +} + +var kindTests = map[string][]func(s *KindSuite){} + +func RegisterKindTests(tests ...func(s *KindSuite)) { + kindTests[GetTestFilename()] = tests +} + +func (s *KindSuite) SetupTest() { + s.HstCommon.SetupTest() +} + +func (s *KindSuite) SetupSuite() { + s.HstCommon.SetupSuite() + RegisterFailHandler(func(message string, callerSkip ...int) { + Fail(message, callerSkip...) + }) + + s.initPods() + s.loadDockerImages() + + var err error + if *SudoUser == "root" { + s.KubeconfigPath = "/.kube/config" + } else { + s.KubeconfigPath = "/home/" + *SudoUser + "/.kube/config" + } + + s.Config, err = clientcmd.BuildConfigFromFlags("", s.KubeconfigPath) + s.AssertNil(err) + + s.ClientSet, err = kubernetes.NewForConfig(s.Config) + s.AssertNil(err) + + s.createNamespace(s.Namespace) +} + +func (s *KindSuite) TeardownTest() { + s.HstCommon.TeardownTest() + if len(s.CurrentlyRunning) != 0 { + for _, pod := range s.CurrentlyRunning { + s.Log(" %s", pod) + s.deletePod(s.Namespace, pod) + } + } +} + +func (s *KindSuite) TeardownSuite() { + s.Log(" %s", s.Namespace) + s.AssertNil(s.deleteNamespace(s.Namespace)) +} + +func (s *KindSuite) CreateConfigFromTemplate(targetConfigName string, templateName string, values any) { + template := template.Must(template.ParseFiles(templateName)) + + f, err := os.CreateTemp(LogDir, "hst-config") + s.AssertNil(err, err) + defer os.Remove(f.Name()) + + err = template.Execute(f, values) + s.AssertNil(err, err) + + err = f.Close() + s.AssertNil(err, err) + + s.CopyToPod(s.Pods.Nginx.Name, s.Namespace, f.Name(), targetConfigName) +} + +func (s *KindSuite) CreateNginxConfig() { + values := struct { + Workers uint8 + }{ + Workers: 1, + } + s.CreateConfigFromTemplate( + "/nginx.conf", + "./resources/nginx/nginx.conf", + values, + ) +} + +var _ = Describe("KindSuite", Ordered, ContinueOnFailure, Label("Perf"), func() { + var s KindSuite + BeforeAll(func() { + s.SetupSuite() + }) + BeforeEach(func() { + s.SetupTest() + }) + AfterEach(func() { + s.TeardownTest() + }) + AfterAll(func() { + s.TeardownSuite() + }) + + for filename, tests := range kindTests { + 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(TestTimeout)) + } + } +}) diff --git a/extras/hs-test/infra/infra_kind/utils.go b/extras/hs-test/infra/kind/utils.go similarity index 65% rename from extras/hs-test/infra/infra_kind/utils.go rename to extras/hs-test/infra/kind/utils.go index 9e6f0e60117..d6f37194b8a 100644 --- a/extras/hs-test/infra/infra_kind/utils.go +++ b/extras/hs-test/infra/kind/utils.go @@ -1,4 +1,4 @@ -package hst +package hst_kind import ( "bytes" @@ -15,16 +15,16 @@ func (s *KindSuite) CopyToPod(podName string, namespace string, src string, dst s.AssertNil(err, string(out)) } -func (s *KindSuite) Exec(podName string, containerName string, command []string) (string, error) { +func (s *KindSuite) Exec(pod *Pod, command []string) (string, error) { var stdout, stderr bytes.Buffer // Prepare the request req := s.ClientSet.CoreV1().RESTClient().Post(). Resource("pods"). - Name(podName). + Name(pod.Name). Namespace(s.Namespace). SubResource("exec"). - Param("container", containerName). + Param("container", pod.ContainerName). Param("stdout", "true"). Param("stderr", "true"). Param("tty", "true") @@ -32,7 +32,7 @@ func (s *KindSuite) Exec(podName string, containerName string, command []string) for _, cmd := range command { req = req.Param("command", cmd) } - s.Log("%s: %s", podName, command) + s.Log("%s: %s", pod.Name, command) executor, err := remotecommand.NewSPDYExecutor(s.Config, "POST", req.URL()) if err != nil { @@ -57,24 +57,6 @@ func (s *KindSuite) Exec(podName string, containerName string, command []string) return output, nil } -// Alternative exec function. Use if regular Exec() doesn't work. -func (s *KindSuite) ExecAlt(podName string, containerName string, namespace string, command []string) (string, error) { - baseCmd := []string{ - "kubectl", - "--kubeconfig=" + s.KubeconfigPath, - "-n", namespace, - "exec", - podName, - "--", - } - fullCmd := append(baseCmd, command...) - cmd := exec.Command(fullCmd[0], fullCmd[1:]...) - s.Log(cmd) - out, err := cmd.CombinedOutput() - - return string(out), err -} - func boolPtr(b bool) *bool { return &b } diff --git a/extras/hs-test/infra/suite_cpu_pinning.go b/extras/hs-test/infra/suite_cpu_pinning.go index 02ea6546e13..6808768d49c 100644 --- a/extras/hs-test/infra/suite_cpu_pinning.go +++ b/extras/hs-test/infra/suite_cpu_pinning.go @@ -6,6 +6,7 @@ import ( "runtime" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -56,11 +57,11 @@ func (s *CpuPinningSuite) SetupTest() { } } -func (s *CpuPinningSuite) TearDownTest() { +func (s *CpuPinningSuite) TeardownTest() { // reset vars s.CpuCount = *NConfiguredCpus s.CpuAllocator.maxContainerCount = s.previousMaxContainerCount - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } @@ -73,11 +74,11 @@ var _ = Describe("CpuPinningSuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs @@ -104,10 +105,10 @@ var _ = Describe("CpuPinningSuiteSolo", Ordered, ContinueOnFailure, Serial, func s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range cpuPinningSoloTests { diff --git a/extras/hs-test/infra/suite_envoy_proxy.go b/extras/hs-test/infra/suite_envoy_proxy.go index a49ec7a7e35..79d9372588e 100644 --- a/extras/hs-test/infra/suite_envoy_proxy.go +++ b/extras/hs-test/infra/suite_envoy_proxy.go @@ -12,6 +12,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -142,12 +143,12 @@ func (s *EnvoyProxySuite) SetupTest() { s.AssertNil(s.Containers.EnvoyProxy.Start()) } -func (s *EnvoyProxySuite) TearDownTest() { +func (s *EnvoyProxySuite) TeardownTest() { if CurrentSpecReport().Failed() { s.CollectNginxLogs(s.Containers.NginxServerTransient) s.CollectEnvoyLogs(s.Containers.EnvoyProxy) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *EnvoyProxySuite) ProxyPort() uint16 { @@ -182,10 +183,10 @@ var _ = Describe("EnvoyProxySuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range envoyProxyTests { @@ -211,10 +212,10 @@ var _ = Describe("EnvoyProxySuiteSolo", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range envoyProxySoloTests { diff --git a/extras/hs-test/infra/suite_h2.go b/extras/hs-test/infra/suite_h2.go index 246a98ed0e9..7cf7a6b43ef 100644 --- a/extras/hs-test/infra/suite_h2.go +++ b/extras/hs-test/infra/suite_h2.go @@ -9,14 +9,14 @@ import ( "strings" "time" - "github.com/summerwind/h2spec/spec" - "fd.io/hs-test/h2spec_extras" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" "github.com/summerwind/h2spec/config" "github.com/summerwind/h2spec/generic" "github.com/summerwind/h2spec/hpack" "github.com/summerwind/h2spec/http2" + "github.com/summerwind/h2spec/spec" ) var h2Tests = map[string][]func(s *H2Suite){} @@ -67,8 +67,8 @@ func (s *H2Suite) SetupTest() { } } -func (s *H2Suite) TearDownTest() { - s.HstSuite.TearDownTest() +func (s *H2Suite) TeardownTest() { + s.HstSuite.TeardownTest() } func (s *H2Suite) VppAddr() string { @@ -85,10 +85,10 @@ var _ = Describe("Http2Suite", Pending, Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range h2Tests { @@ -307,10 +307,10 @@ var _ = Describe("H2SpecSuite", Pending, Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for _, sp := range specs { diff --git a/extras/hs-test/infra/suite_iperf_linux.go b/extras/hs-test/infra/suite_iperf_linux.go index 440773b416c..10cedf73371 100644 --- a/extras/hs-test/infra/suite_iperf_linux.go +++ b/extras/hs-test/infra/suite_iperf_linux.go @@ -6,6 +6,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -51,10 +52,10 @@ var _ = Describe("IperfSuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range iperfTests { @@ -80,10 +81,10 @@ var _ = Describe("IperfSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range iperfSoloTests { diff --git a/extras/hs-test/infra/suite_ldp.go b/extras/hs-test/infra/suite_ldp.go index 5ef2e6d14df..bdc718e8978 100644 --- a/extras/hs-test/infra/suite_ldp.go +++ b/extras/hs-test/infra/suite_ldp.go @@ -7,6 +7,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -96,7 +97,7 @@ func (s *LdpSuite) SetupTest() { } } -func (s *LdpSuite) TearDownTest() { +func (s *LdpSuite) TeardownTest() { if CurrentSpecReport().Failed() { s.CollectIperfLogs(s.Containers.ServerVpp) s.CollectRedisServerLogs(s.Containers.ServerVpp) @@ -106,7 +107,7 @@ func (s *LdpSuite) TearDownTest() { delete(container.EnvVars, "LD_PRELOAD") delete(container.EnvVars, "VCL_CONFIG") } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } @@ -155,11 +156,11 @@ var _ = Describe("LdpSuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs @@ -186,10 +187,10 @@ var _ = Describe("LdpSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs diff --git a/extras/hs-test/infra/suite_nginx_proxy.go b/extras/hs-test/infra/suite_nginx_proxy.go index af8b004c731..a259db36b58 100644 --- a/extras/hs-test/infra/suite_nginx_proxy.go +++ b/extras/hs-test/infra/suite_nginx_proxy.go @@ -6,6 +6,7 @@ import ( "runtime" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -101,12 +102,12 @@ func (s *NginxProxySuite) SetupTest() { s.AssertNil(s.Containers.NginxServerTransient.Start()) } -func (s *NginxProxySuite) TearDownTest() { +func (s *NginxProxySuite) TeardownTest() { if CurrentSpecReport().Failed() { s.CollectNginxLogs(s.Containers.NginxProxy) s.CollectNginxLogs(s.Containers.NginxServerTransient) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *NginxProxySuite) CreateNginxProxyConfig(container *Container, multiThreadWorkers bool) { @@ -186,10 +187,10 @@ var _ = Describe("NginxProxySuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range nginxProxyTests { @@ -215,10 +216,10 @@ var _ = Describe("NginxProxySuiteSolo", Ordered, ContinueOnFailure, Serial, func s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range nginxProxySoloTests { diff --git a/extras/hs-test/infra/suite_no_topo.go b/extras/hs-test/infra/suite_no_topo.go index fbc7d80a42a..dd77c157e60 100644 --- a/extras/hs-test/infra/suite_no_topo.go +++ b/extras/hs-test/infra/suite_no_topo.go @@ -6,6 +6,7 @@ import ( "runtime" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -78,11 +79,11 @@ func (s *NoTopoSuite) SetupTest() { } } -func (s *NoTopoSuite) TearDownTest() { +func (s *NoTopoSuite) TeardownTest() { if CurrentSpecReport().Failed() { s.CollectNginxLogs(s.Containers.NginxHttp3) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *NoTopoSuite) CreateNginxConfig(container *Container, multiThreadWorkers bool) { @@ -184,10 +185,10 @@ var _ = Describe("NoTopoSuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range noTopoTests { @@ -213,10 +214,10 @@ var _ = Describe("NoTopoSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range noTopoSoloTests { diff --git a/extras/hs-test/infra/suite_no_topo6.go b/extras/hs-test/infra/suite_no_topo6.go index 7ccf97b5062..ac617c8fcfd 100644 --- a/extras/hs-test/infra/suite_no_topo6.go +++ b/extras/hs-test/infra/suite_no_topo6.go @@ -6,6 +6,7 @@ import ( "runtime" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -78,11 +79,11 @@ func (s *NoTopo6Suite) SetupTest() { } } -func (s *NoTopo6Suite) TearDownTest() { +func (s *NoTopo6Suite) TeardownTest() { if CurrentSpecReport().Failed() { s.CollectNginxLogs(s.Containers.NginxHttp3) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *NoTopo6Suite) CreateNginxConfig(container *Container, multiThreadWorkers bool) { @@ -184,10 +185,10 @@ var _ = Describe("NoTopo6Suite", Ordered, ContinueOnFailure, Label("IPv6"), func s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range noTopo6Tests { @@ -213,10 +214,10 @@ var _ = Describe("NoTopo6SuiteSolo", Ordered, ContinueOnFailure, Serial, Label(" s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range noTopo6SoloTests { diff --git a/extras/hs-test/infra/suite_veth.go b/extras/hs-test/infra/suite_veth.go index b2db34e6d15..4dc6b5993bf 100644 --- a/extras/hs-test/infra/suite_veth.go +++ b/extras/hs-test/infra/suite_veth.go @@ -7,6 +7,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -107,11 +108,11 @@ var _ = Describe("VethsSuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs @@ -138,10 +139,10 @@ var _ = Describe("VethsSuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs diff --git a/extras/hs-test/infra/suite_veth6.go b/extras/hs-test/infra/suite_veth6.go index 8b588f0f1e4..694972c406a 100644 --- a/extras/hs-test/infra/suite_veth6.go +++ b/extras/hs-test/infra/suite_veth6.go @@ -7,6 +7,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -107,11 +108,11 @@ var _ = Describe("Veths6Suite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs @@ -138,10 +139,10 @@ var _ = Describe("Veths6SuiteSolo", Ordered, ContinueOnFailure, Serial, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) // https://onsi.github.io/ginkgo/#dynamically-generating-specs diff --git a/extras/hs-test/infra/suite_vpp_proxy.go b/extras/hs-test/infra/suite_vpp_proxy.go index d8cc4662c13..b0881993d81 100644 --- a/extras/hs-test/infra/suite_vpp_proxy.go +++ b/extras/hs-test/infra/suite_vpp_proxy.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -87,7 +88,7 @@ func (s *VppProxySuite) SetupTest() { } } -func (s *VppProxySuite) TearDownTest() { +func (s *VppProxySuite) TeardownTest() { vpp := s.Containers.VppProxy.VppInstance if CurrentSpecReport().Failed() { s.Log(vpp.Vppctl("show session verbose 2")) @@ -95,7 +96,7 @@ func (s *VppProxySuite) TearDownTest() { s.CollectNginxLogs(s.Containers.NginxServerTransient) s.CollectIperfLogs(s.Containers.IperfS) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *VppProxySuite) SetupNginxServer() { @@ -221,10 +222,10 @@ var _ = Describe("VppProxySuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range vppProxyTests { @@ -250,10 +251,10 @@ var _ = Describe("VppProxySuiteSolo", Ordered, ContinueOnFailure, Serial, func() s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range vppProxySoloTests { diff --git a/extras/hs-test/infra/suite_vpp_udp_proxy.go b/extras/hs-test/infra/suite_vpp_udp_proxy.go index 7a4a5f3add5..b2157757087 100644 --- a/extras/hs-test/infra/suite_vpp_udp_proxy.go +++ b/extras/hs-test/infra/suite_vpp_udp_proxy.go @@ -9,6 +9,7 @@ import ( "strings" "time" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) @@ -85,13 +86,13 @@ func (s *VppUdpProxySuite) SetupTest() { } } -func (s *VppUdpProxySuite) TearDownTest() { +func (s *VppUdpProxySuite) TeardownTest() { vpp := s.Containers.VppProxy.VppInstance if CurrentSpecReport().Failed() { s.Log(vpp.Vppctl("show session verbose 2")) s.Log(vpp.Vppctl("show error")) } - s.HstSuite.TearDownTest() + s.HstSuite.TeardownTest() } func (s *VppUdpProxySuite) VppProxyAddr() string { @@ -168,10 +169,10 @@ var _ = Describe("VppUdpProxySuite", Ordered, ContinueOnFailure, func() { s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range vppUdpProxyTests { @@ -197,10 +198,10 @@ var _ = Describe("VppUdpProxySuiteSolo", Ordered, ContinueOnFailure, Serial, fun s.SetupTest() }) AfterAll(func() { - s.TearDownSuite() + s.TeardownSuite() }) AfterEach(func() { - s.TearDownTest() + s.TeardownTest() }) for filename, tests := range vppUdpProxySoloTests { diff --git a/extras/hs-test/infra/utils.go b/extras/hs-test/infra/utils.go index 8b61320db41..5285ba8b581 100644 --- a/extras/hs-test/infra/utils.go +++ b/extras/hs-test/infra/utils.go @@ -2,7 +2,6 @@ package hst import ( "crypto/tls" - "encoding/json" "errors" "fmt" "io" @@ -41,43 +40,6 @@ type JsonResult struct { StdOutput string } -type IPerfResult struct { - Start struct { - Timestamp struct { - Time string `json:"time"` - } `json:"timestamp"` - Connected []struct { - Socket int `json:"socket"` - LocalHost string `json:"local_host"` - LocalPort int `json:"local_port"` - RemoteHost string `json:"remote_host"` - RemotePort int `json:"remote_port"` - } `json:"connected"` - Version string `json:"version"` - Details struct { - Protocol string `json:"protocol"` - } `json:"test_start"` - } `json:"start"` - End struct { - TcpSent *struct { - MbitsPerSecond float64 `json:"bits_per_second"` - MBytes float64 `json:"bytes"` - } `json:"sum_sent,omitempty"` - TcpReceived *struct { - MbitsPerSecond float64 `json:"bits_per_second"` - MBytes float64 `json:"bytes"` - } `json:"sum_received,omitempty"` - Udp *struct { - MbitsPerSecond float64 `json:"bits_per_second"` - JitterMs float64 `json:"jitter_ms,omitempty"` - LostPackets int `json:"lost_packets,omitempty"` - Packets int `json:"packets,omitempty"` - LostPercent float64 `json:"lost_percent,omitempty"` - MBytes float64 `json:"bytes"` - } `json:"sum,omitempty"` - } `json:"end"` -} - func AssertFileSize(f1, f2 string) error { fi1, err := os.Stat(f1) if err != nil { @@ -393,64 +355,3 @@ func (s *HstSuite) StartClientApp(c *Container, cmd string, clnRes <- o } } - -func (s *HstSuite) ParseJsonIperfOutput(jsonResult []byte) IPerfResult { - var result IPerfResult - // remove iperf warning line if present - if strings.Contains(string(jsonResult), "warning") { - index := strings.Index(string(jsonResult), "\n") - jsonResult = jsonResult[index+1:] - } - - err := json.Unmarshal(jsonResult, &result) - s.AssertNil(err) - - if result.Start.Details.Protocol == "TCP" { - result.End.TcpSent.MbitsPerSecond = result.End.TcpSent.MbitsPerSecond / 1000000 - result.End.TcpSent.MBytes = result.End.TcpSent.MBytes / 1000000 - result.End.TcpReceived.MbitsPerSecond = result.End.TcpReceived.MbitsPerSecond / 1000000 - result.End.TcpReceived.MBytes = result.End.TcpReceived.MBytes / 1000000 - } else { - result.End.Udp.MBytes = result.End.Udp.MBytes / 1000000 - result.End.Udp.MbitsPerSecond = result.End.Udp.MbitsPerSecond / 1000000 - } - - return result -} - -func (s *HstSuite) LogJsonIperfOutput(result IPerfResult) { - s.Log("\n*******************************************\n"+ - "%s\n"+ - "[%s] %s:%d connected to %s:%d\n"+ - "Started: %s\n", - result.Start.Version, - result.Start.Details.Protocol, - result.Start.Connected[0].LocalHost, result.Start.Connected[0].LocalPort, - result.Start.Connected[0].RemoteHost, result.Start.Connected[0].RemotePort, - result.Start.Timestamp.Time) - - if result.Start.Details.Protocol == "TCP" { - s.Log("Transfer (sent): %.2f MBytes\n"+ - "Bitrate (sent): %.2f Mbits/sec\n"+ - "Transfer (received): %.2f MBytes\n"+ - "Bitrate (received): %.2f Mbits/sec", - result.End.TcpSent.MBytes, - result.End.TcpSent.MbitsPerSecond, - result.End.TcpReceived.MBytes, - result.End.TcpReceived.MbitsPerSecond) - } else { - s.Log("Transfer: %.2f MBytes\n"+ - "Bitrate: %.2f Mbits/sec\n"+ - "Jitter: %.3f ms\n"+ - "Packets: %d\n"+ - "Packets lost: %d\n"+ - "Percent lost: %.2f%%", - result.End.Udp.MBytes, - result.End.Udp.MbitsPerSecond, - result.End.Udp.JitterMs, - result.End.Udp.Packets, - result.End.Udp.LostPackets, - result.End.Udp.LostPercent) - } - s.Log("*******************************************\n") -} diff --git a/extras/hs-test/infra/vppinstance.go b/extras/hs-test/infra/vppinstance.go index 4ea7686a1d8..501eb417c65 100644 --- a/extras/hs-test/infra/vppinstance.go +++ b/extras/hs-test/infra/vppinstance.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + . "fd.io/hs-test/infra/common" "go.fd.io/govpp/binapi/ethernet_types" "github.com/edwarnicke/exechelper" @@ -272,7 +273,7 @@ func (vpp *VppInstance) Vppctl(command string, arguments ...any) string { vpp.getSuite().AssertNil(err) } else { fn := runtime.FuncForPC(pc) - if fn != nil && strings.Contains(fn.Name(), "TearDownTest") { + if fn != nil && strings.Contains(fn.Name(), "TeardownTest") { vpp.getSuite().Log("vppctl failed in test teardown (skipping assert): %v", err) } else { vpp.getSuite().AssertNil(err) diff --git a/extras/hs-test/kind_test.go b/extras/hs-test/kind_test.go index a45979fd725..b1e2d23b6e9 100644 --- a/extras/hs-test/kind_test.go +++ b/extras/hs-test/kind_test.go @@ -3,7 +3,7 @@ package main import ( "time" - . "fd.io/hs-test/infra/infra_kind" + . "fd.io/hs-test/infra/kind" . "github.com/onsi/ginkgo/v2" ) @@ -12,7 +12,8 @@ func init() { } func KindIperfVclTest(s *KindSuite) { - s.DeployServerClient(s.ImageNames.HstVpp, s.ImageNames.HstVpp, s.PodNames.ServerVpp, s.PodNames.ClientVpp) + s.DeployPod(s.Pods.ClientGeneric) + s.DeployPod(s.Pods.ServerGeneric) vclPath := "/vcl.conf" ldpPath := "/usr/lib/libvcl_ldpreload.so" @@ -34,25 +35,26 @@ func KindIperfVclTest(s *KindSuite) { "app-socket-api abstract:vpp/session\n" + "}\" > /vcl.conf" - s.Exec(s.PodNames.ClientVpp, s.ContainerNames.Client, []string{"/bin/bash", "-c", symLink}) - s.Exec(s.PodNames.ServerVpp, s.ContainerNames.Server, []string{"/bin/bash", "-c", symLink}) + s.Exec(s.Pods.ClientGeneric, []string{"/bin/bash", "-c", symLink}) + s.Exec(s.Pods.ServerGeneric, []string{"/bin/bash", "-c", symLink}) - _, err := s.Exec(s.PodNames.ClientVpp, s.ContainerNames.Client, []string{"/bin/bash", "-c", vclConf}) + _, err := s.Exec(s.Pods.ClientGeneric, []string{"/bin/bash", "-c", vclConf}) s.AssertNil(err) - _, err = s.Exec(s.PodNames.ServerVpp, s.ContainerNames.Server, []string{"/bin/bash", "-c", vclConf}) + _, err = s.Exec(s.Pods.ServerGeneric, []string{"/bin/bash", "-c", vclConf}) s.AssertNil(err) - _, err = s.Exec(s.PodNames.ServerVpp, s.ContainerNames.Server, []string{"/bin/bash", "-c", + _, err = s.Exec(s.Pods.ServerGeneric, []string{"/bin/bash", "-c", "VCL_CONFIG=" + vclPath + " LD_PRELOAD=" + ldpPath + " iperf3 -s -D -4"}) s.AssertNil(err) - output, err := s.Exec(s.PodNames.ClientVpp, s.ContainerNames.Client, []string{"/bin/bash", "-c", - "VCL_CONFIG=" + vclPath + " LD_PRELOAD=" + ldpPath + " iperf3 -c " + s.ServerIp}) + output, err := s.Exec(s.Pods.ClientGeneric, []string{"/bin/bash", "-c", + "VCL_CONFIG=" + vclPath + " LD_PRELOAD=" + ldpPath + " iperf3 -c " + s.Pods.ServerGeneric.IpAddress}) s.Log(output) s.AssertNil(err) } func NginxRpsTest(s *KindSuite) { - s.DeployServerClient(s.ImageNames.Nginx, s.ImageNames.Ab, s.PodNames.Nginx, s.PodNames.Ab) + s.DeployPod(s.Pods.Nginx) + s.DeployPod(s.Pods.Ab) s.CreateNginxConfig() vcl := "VCL_CONFIG=/vcl.conf" ldp := "LD_PRELOAD=/usr/lib/libvcl_ldpreload.so" @@ -77,21 +79,21 @@ func NginxRpsTest(s *KindSuite) { "app-socket-api abstract:vpp/session\n" + "}\" > /vcl.conf" - out, err := s.Exec(s.PodNames.Nginx, s.ContainerNames.Server, []string{"/bin/bash", "-c", symLink}) + out, err := s.Exec(s.Pods.Nginx, []string{"/bin/bash", "-c", symLink}) s.AssertNil(err, out) - out, err = s.Exec(s.PodNames.Nginx, s.ContainerNames.Server, []string{"/bin/bash", "-c", vclConf}) + out, err = s.Exec(s.Pods.Nginx, []string{"/bin/bash", "-c", vclConf}) s.AssertNil(err, out) go func() { defer GinkgoRecover() - out, err := s.Exec(s.PodNames.Nginx, s.ContainerNames.Server, []string{"/bin/bash", "-c", ldp + " " + vcl + " nginx -c /nginx.conf"}) + out, err := s.Exec(s.Pods.Nginx, []string{"/bin/bash", "-c", ldp + " " + vcl + " nginx -c /nginx.conf"}) s.AssertNil(err, out) }() // wait for nginx to start up time.Sleep(time.Second * 2) - out, err = s.Exec(s.PodNames.Ab, s.ContainerNames.Client, []string{"ab", "-k", "-r", "-n", "1000000", "-c", "1000", "http://" + s.ServerIp + ":80/64B.json"}) + out, err = s.Exec(s.Pods.Ab, []string{"ab", "-k", "-r", "-n", "1000000", "-c", "1000", "http://" + s.Pods.Nginx.IpAddress + ":80/64B.json"}) s.Log(out) s.AssertNil(err) } diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go index eaabe599f3d..0943a16d0eb 100644 --- a/extras/hs-test/ldp_test.go +++ b/extras/hs-test/ldp_test.go @@ -6,6 +6,7 @@ import ( "time" . "fd.io/hs-test/infra" + . "fd.io/hs-test/infra/common" . "github.com/onsi/ginkgo/v2" ) -- 2.16.6