hs-test: further separated KinD from HST suites 82/42882/6
authorAdrian Villin <avillin@cisco.com>
Mon, 12 May 2025 13:16:18 +0000 (15:16 +0200)
committerFlorin Coras <florin.coras@gmail.com>
Tue, 20 May 2025 20:49:01 +0000 (20:49 +0000)
- KinD suite was also improved and cleaned up a bit

Type: test

Change-Id: Iee1adb69f935b69b79d70a541fa40f5f5d73298d
Signed-off-by: Adrian Villin <avillin@cisco.com>
29 files changed:
extras/hs-test/README.rst
extras/hs-test/framework_test.go
extras/hs-test/infra/common/suite_common.go [new file with mode: 0644]
extras/hs-test/infra/common/utils_common.go [new file with mode: 0644]
extras/hs-test/infra/container.go
extras/hs-test/infra/cpu.go
extras/hs-test/infra/hst_suite.go
extras/hs-test/infra/infra_kind/deployment.go [deleted file]
extras/hs-test/infra/infra_kind/suite_kind.go [deleted file]
extras/hs-test/infra/kind/deployment.go [new file with mode: 0644]
extras/hs-test/infra/kind/pod.go [new file with mode: 0644]
extras/hs-test/infra/kind/suite_kind.go [new file with mode: 0644]
extras/hs-test/infra/kind/utils.go [moved from extras/hs-test/infra/infra_kind/utils.go with 65% similarity]
extras/hs-test/infra/suite_cpu_pinning.go
extras/hs-test/infra/suite_envoy_proxy.go
extras/hs-test/infra/suite_h2.go
extras/hs-test/infra/suite_iperf_linux.go
extras/hs-test/infra/suite_ldp.go
extras/hs-test/infra/suite_nginx_proxy.go
extras/hs-test/infra/suite_no_topo.go
extras/hs-test/infra/suite_no_topo6.go
extras/hs-test/infra/suite_veth.go
extras/hs-test/infra/suite_veth6.go
extras/hs-test/infra/suite_vpp_proxy.go
extras/hs-test/infra/suite_vpp_udp_proxy.go
extras/hs-test/infra/utils.go
extras/hs-test/infra/vppinstance.go
extras/hs-test/kind_test.go
extras/hs-test/ldp_test.go

index 8deebe3..ca9f1f9 100644 (file)
@@ -22,7 +22,7 @@ Anatomy of a test case
 * Install hs-test dependencies with ``make install-deps``
 * `Install Go <https://go.dev/doc/install>`_, 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 <eventviewer>` or ``convert_evt`` tool, located in ``src/scripts/host-stack/``,
 which convert event logs to human readable text.
index 66c5e50..962d6d5 100644 (file)
@@ -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 (file)
index 0000000..72173c6
--- /dev/null
@@ -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 (file)
index 0000000..5b3d718
--- /dev/null
@@ -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")
+}
index 341c925..5bb8040 100644 (file)
@@ -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
index 4afc96b..d6ae14e 100644 (file)
@@ -8,6 +8,7 @@ import (
        "os/exec"
        "strings"
 
+       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )
 
index 49b1aa9..03eaff5 100644 (file)
@@ -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 (file)
index baa665b..0000000
+++ /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 (file)
index eac334e..0000000
+++ /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 (file)
index 0000000..d28ffc8
--- /dev/null
@@ -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 (file)
index 0000000..1b37047
--- /dev/null
@@ -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 (file)
index 0000000..ffbd830
--- /dev/null
@@ -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))
+               }
+       }
+})
similarity index 65%
rename from extras/hs-test/infra/infra_kind/utils.go
rename to extras/hs-test/infra/kind/utils.go
index 9e6f0e6..d6f3719 100644 (file)
@@ -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
 }
index 02ea654..6808768 100644 (file)
@@ -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 {
index a49ec7a..79d9372 100644 (file)
@@ -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 {
index 246a98e..7cf7a6b 100644 (file)
@@ -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 {
index 440773b..10cedf7 100644 (file)
@@ -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 {
index 5ef2e6d..bdc718e 100644 (file)
@@ -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
index af8b004..a259db3 100644 (file)
@@ -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 {
index fbc7d80..dd77c15 100644 (file)
@@ -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 {
index 7ccf97b..ac617c8 100644 (file)
@@ -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 {
index b2db34e..4dc6b59 100644 (file)
@@ -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
index 8b588f0..694972c 100644 (file)
@@ -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
index d8cc466..b088199 100644 (file)
@@ -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 {
index 7a4a5f3..b215775 100644 (file)
@@ -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 {
index 8b61320..5285ba8 100644 (file)
@@ -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")
-}
index 4ea7686..501eb41 100644 (file)
@@ -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)
index a45979f..b1e2d23 100644 (file)
@@ -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)
 }
index eaabe59..0943a16 100644 (file)
@@ -6,6 +6,7 @@ import (
        "time"
 
        . "fd.io/hs-test/infra"
+       . "fd.io/hs-test/infra/common"
        . "github.com/onsi/ginkgo/v2"
 )