hs-test: configure VPP from test context
Instead of configuring VPP instances running inside of a container,
now the configuration is going to be done from within the test context
by using binary API and shared volume that exposes api socket.
This converts just some of the test cases, rest is to follow.
Type: test
Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech>
Change-Id: I87e4ab15de488f0eebb01ff514596265fc2a787f
diff --git a/extras/hs-test/actions.go b/extras/hs-test/actions.go
index 279589b..6039870 100644
--- a/extras/hs-test/actions.go
+++ b/extras/hs-test/actions.go
@@ -1,7 +1,6 @@
package main
import (
- "bytes"
"context"
"fmt"
"os"
@@ -47,17 +46,6 @@
}
}
-func (a *Actions) RunHttpCliSrv(args []string) *ActionResult {
- cmd := fmt.Sprintf("http cli server")
- return ApiCliInband(workDir, cmd)
-}
-
-func (a *Actions) RunHttpCliCln(args []string) *ActionResult {
- cmd := fmt.Sprintf("http cli client uri http://10.10.10.1/80 query %s", getArgs())
- fmt.Println(cmd)
- return ApiCliInband(workDir, cmd)
-}
-
func (a *Actions) ConfigureVppProxy(args []string) *ActionResult {
ctx, cancel := newVppContext()
defer cancel()
@@ -123,31 +111,6 @@
return NewActionResult(err, ActionResultWithStdout(cliInbandReply.Reply))
}
-func (a *Actions) RunEchoClient(args []string) *ActionResult {
- outBuff := bytes.NewBuffer([]byte{})
- errBuff := bytes.NewBuffer([]byte{})
-
- cmd := fmt.Sprintf("vpp_echo client socket-name %s/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
- err := exechelper.Run(cmd,
- exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
- exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))
-
- return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
- ActionResultWithStderr(string(errBuff.String())))
-}
-
-func (a *Actions) RunEchoServer(args []string) *ActionResult {
- cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name %s/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", workDir, args[2])
- errCh := exechelper.Start(cmd)
- select {
- case err := <-errCh:
- writeSyncFile(NewActionResult(err, ActionResultWithDesc("echo_server: ")))
- default:
- }
- writeSyncFile(OkResult())
- return nil
-}
-
func (a *Actions) RunEchoSrvInternal(args []string) *ActionResult {
cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs())
return ApiCliInband(workDir, cmd)
@@ -158,49 +121,6 @@
return ApiCliInband(workDir, cmd)
}
-func (a *Actions) RunVclEchoServer(args []string) *ActionResult {
- f, err := os.Create("vcl_1.conf")
- if err != nil {
- return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
- }
- socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/1", workDir)
- fmt.Fprintf(f, vclTemplate, socketPath, "1")
- f.Close()
-
- os.Setenv("VCL_CONFIG", "./vcl_1.conf")
- cmd := fmt.Sprintf("vcl_test_server -p %s 12346", args[2])
- errCh := exechelper.Start(cmd)
- select {
- case err := <-errCh:
- writeSyncFile(NewActionResult(err, ActionResultWithDesc("vcl_test_server: ")))
- default:
- }
- writeSyncFile(OkResult())
- return nil
-}
-
-func (a *Actions) RunVclEchoClient(args []string) *ActionResult {
- outBuff := bytes.NewBuffer([]byte{})
- errBuff := bytes.NewBuffer([]byte{})
-
- f, err := os.Create("vcl_2.conf")
- if err != nil {
- return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
- }
- socketPath := fmt.Sprintf("%s/var/run/app_ns_sockets/2", workDir)
- fmt.Fprintf(f, vclTemplate, socketPath, "2")
- f.Close()
-
- os.Setenv("VCL_CONFIG", "./vcl_2.conf")
- cmd := fmt.Sprintf("vcl_test_client -U -p %s 10.10.10.1 12346", args[2])
- err = exechelper.Run(cmd,
- exechelper.WithStdout(outBuff), exechelper.WithStderr(errBuff),
- exechelper.WithStdout(os.Stdout), exechelper.WithStderr(os.Stderr))
-
- return NewActionResult(err, ActionResultWithStdout(string(outBuff.String())),
- ActionResultWithStderr(string(errBuff.String())))
-}
-
func configure2vethsTopo(ifName, interfaceAddress, namespaceId string, secret uint64, optionalHardwareAddress ...string) ConfFn {
return func(ctx context.Context,
vppConn api.Connection) error {
diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go
index 971f3b6..91ca2c2 100644
--- a/extras/hs-test/container.go
+++ b/extras/hs-test/container.go
@@ -3,24 +3,27 @@
import (
"fmt"
"os"
+ "os/exec"
"strings"
"github.com/edwarnicke/exechelper"
)
type Volume struct {
- hostDir string
- containerDir string
+ hostDir string
+ containerDir string
+ isDefaultWorkDir bool
}
type Container struct {
+ suite *HstSuite
isOptional bool
name string
image string
- workDir string
extraRunningArgs string
volumes map[string]Volume
envVars map[string]string
+ vppInstance *VppInstance
}
func NewContainer(yamlInput ContainerConfig) (*Container, error) {
@@ -59,25 +62,53 @@
volumeMap := volu.(ContainerConfig)
hostDir := r.Replace(volumeMap["host-dir"].(string))
containerDir := volumeMap["container-dir"].(string)
- container.addVolume(hostDir, containerDir)
+ isDefaultWorkDir := false
- if isDefaultWorkDir, ok := volumeMap["is-default-work-dir"]; ok &&
- isDefaultWorkDir.(bool) &&
- len(container.workDir) == 0 {
- container.workDir = containerDir
+ if isDefault, ok := volumeMap["is-default-work-dir"]; ok {
+ isDefaultWorkDir = isDefault.(bool)
}
+ container.addVolume(hostDir, containerDir, isDefaultWorkDir)
+
}
}
if _, ok := yamlInput["vars"]; ok {
for _, envVar := range yamlInput["vars"].([]interface{}) {
- container.addEnvVar(envVar)
+ envVarMap := envVar.(ContainerConfig)
+ name := envVarMap["name"].(string)
+ value := envVarMap["value"].(string)
+ container.addEnvVar(name, value)
}
}
return container, nil
}
+func (c *Container) getWorkDirVolume() (res Volume, exists bool) {
+ for _, v := range c.volumes {
+ if v.isDefaultWorkDir {
+ res = v
+ exists = true
+ return
+ }
+ }
+ return
+}
+
+func (c *Container) GetHostWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.hostDir
+ }
+ return
+}
+
+func (c *Container) GetContainerWorkDir() (res string) {
+ if v, ok := c.getWorkDirVolume(); ok {
+ res = v.containerDir
+ }
+ return
+}
+
func (c *Container) getRunCommand() string {
syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
cmd := "docker run --cap-add=all -d --privileged --network host --rm"
@@ -103,10 +134,11 @@
return nil
}
-func (c *Container) addVolume(hostDir string, containerDir string) {
+func (c *Container) addVolume(hostDir string, containerDir string, isDefaultWorkDir bool) {
var volume Volume
volume.hostDir = hostDir
volume.containerDir = containerDir
+ volume.isDefaultWorkDir = isDefaultWorkDir
c.volumes[hostDir] = volume
}
@@ -127,16 +159,13 @@
}
func (c *Container) getWorkDirAsCliOption() string {
- if len(c.workDir) == 0 {
- return ""
+ if _, ok := c.getWorkDirVolume(); ok {
+ return fmt.Sprintf(" --workdir=\"%s\"", c.GetContainerWorkDir())
}
- return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
+ return ""
}
-func (c *Container) addEnvVar(envVar interface{}) {
- envVarMap := envVar.(ContainerConfig)
- name := envVarMap["name"].(string)
- value := envVarMap["value"].(string)
+func (c *Container) addEnvVar(name string, value string) {
c.envVars[name] = value
}
@@ -156,8 +185,54 @@
return fmt.Sprintf("/tmp/%s/sync", c.name)
}
+func (c *Container) newVppInstance(additionalConfig ...Stanza) (*VppInstance, error) {
+ vppConfig := new(VppConfig)
+ vppConfig.CliSocketFilePath = defaultCliSocketFilePath
+ if len(additionalConfig) > 0 {
+ vppConfig.additionalConfig = additionalConfig[0]
+ }
+
+ vpp := new(VppInstance)
+ vpp.container = c
+ vpp.config = vppConfig
+
+ c.vppInstance = vpp
+
+ return vpp, nil
+}
+
+func (c *Container) copy(sourceFileName string, targetFileName string) error {
+ cmd := exec.Command("docker", "cp", sourceFileName, c.name+":"+targetFileName)
+ return cmd.Run()
+}
+
+func (c *Container) createFile(destFileName string, content string) error {
+ f, err := os.CreateTemp("/tmp", "hst-config")
+ if err != nil {
+ return err
+ }
+ defer os.Remove(f.Name())
+
+ if _, err := f.Write([]byte(content)); err != nil {
+ return err
+ }
+ if err := f.Close(); err != nil {
+ return err
+ }
+ c.copy(f.Name(), destFileName)
+ return nil
+}
+
+/*
+ * Executes in detached mode so that the started application can continue to run
+ * without blocking execution of test
+ */
+func (c *Container) execServer(command string) error {
+ return exechelper.Run("docker exec -d" + c.getEnvVarsAsCliOption() + " " + c.name + " " + command)
+}
+
func (c *Container) exec(command string) (string, error) {
- cliCommand := "docker exec -d " + c.name + " " + command
+ cliCommand := "docker exec" + c.getEnvVarsAsCliOption() + " " + c.name + " " + command
byteOutput, err := exechelper.CombinedOutput(cliCommand)
return string(byteOutput), err
}
@@ -187,5 +262,9 @@
}
func (c *Container) stop() error {
+ if c.vppInstance != nil && c.vppInstance.apiChannel != nil {
+ c.vppInstance.disconnect()
+ }
+ c.vppInstance = nil
return exechelper.Run("docker stop " + c.name + " -t 0")
}
diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go
index 813297c..1b24e08 100644
--- a/extras/hs-test/echo_test.go
+++ b/extras/hs-test/echo_test.go
@@ -1,18 +1,19 @@
package main
func (s *VethsSuite) TestEchoBuiltin() {
- serverContainer := s.getContainerByName("server-vpp")
- _, err := serverContainer.execAction("Configure2Veths srv")
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+ serverVeth := s.veths["vppsrv"]
+
+ _, err := serverVpp.vppctl("test echo server " +
+ " private-segment-size 1g fifo-size 4 no-echo" +
+ " uri tcp://" + serverVeth.Address() + "/1234")
s.assertNil(err)
- clientContainer := s.getContainerByName("client-vpp")
- _, err = clientContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
- _, err = serverContainer.execAction("RunEchoSrvInternal private-segment-size 1g fifo-size 4 no-echo")
- s.assertNil(err)
-
- o, err := clientContainer.execAction("RunEchoClnInternal nclients 10000 bytes 1 syn-timeout 100 test-timeout 100 no-return private-segment-size 1g fifo-size 4")
+ o, err := clientVpp.vppctl("test echo client nclients 10000 bytes 1" +
+ " syn-timeout 100 test-timeout 100 no-return private-segment-size 1g" +
+ " fifo-size 4 uri tcp://" + serverVeth.Address() + "/1234")
s.assertNil(err)
s.log(o)
}
diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go
index 76d8e58..6de8f16 100644
--- a/extras/hs-test/framework_test.go
+++ b/extras/hs-test/framework_test.go
@@ -1,152 +1,11 @@
package main
import (
- "io/ioutil"
- "os"
"testing"
- "github.com/edwarnicke/exechelper"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "gopkg.in/yaml.v3"
)
-func IsPersistent() bool {
- return os.Getenv("HST_PERSIST") == "1"
-}
-
-func IsVerbose() bool {
- return os.Getenv("HST_VERBOSE") == "1"
-}
-
-type HstSuite struct {
- suite.Suite
- teardownSuite func()
- containers map[string]*Container
- volumes []string
-}
-
-func (s *HstSuite) TearDownSuite() {
- s.teardownSuite()
-}
-
-func (s *HstSuite) TearDownTest() {
- if IsPersistent() {
- return
- }
- s.ResetContainers()
- s.RemoveVolumes()
-}
-
-func (s *HstSuite) SetupTest() {
- for _, volume := range s.volumes {
- cmd := "docker volume create --name=" + volume
- s.log(cmd)
- exechelper.Run(cmd)
- }
- for _, container := range s.containers {
- if container.isOptional == false {
- container.run()
- }
- }
-}
-
-func (s *HstSuite) hstFail() {
- s.T().FailNow()
-}
-
-func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
- if !assert.Nil(s.T(), object, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
- if !assert.NotNil(s.T(), object, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
- if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
- if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
- s.hstFail()
- }
-}
-
-func (s *HstSuite) log(args ...any) {
- if IsVerbose() {
- s.T().Log(args...)
- }
-}
-
-func (s *HstSuite) skip(args ...any) {
- s.log(args...)
- s.T().SkipNow()
-}
-
-func (s *HstSuite) ResetContainers() {
- for _, container := range s.containers {
- container.stop()
- }
-}
-
-func (s *HstSuite) RemoveVolumes() {
- for _, volumeName := range s.volumes {
- cmd := "docker volume rm " + volumeName
- exechelper.Run(cmd)
- }
-}
-
-func (s *HstSuite) getContainerByName(name string) *Container {
- return s.containers[name]
-}
-
-func (s *HstSuite) loadContainerTopology(topologyName string) {
- data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
- if err != nil {
- s.T().Fatalf("read error: %v", err)
- }
- var yamlTopo YamlTopology
- err = yaml.Unmarshal(data, &yamlTopo)
- if err != nil {
- s.T().Fatalf("unmarshal error: %v", err)
- }
-
- for _, elem := range yamlTopo.Volumes {
- volumeMap := elem["volume"].(VolumeConfig)
- hostDir := volumeMap["host-dir"].(string)
- s.volumes = append(s.volumes, hostDir)
- }
-
- s.containers = make(map[string]*Container)
- for _, elem := range yamlTopo.Containers {
- newContainer, err := NewContainer(elem)
- if err != nil {
- s.T().Fatalf("config error: %v", err)
- }
- s.log(newContainer.getRunCommand())
- s.containers[newContainer.name] = newContainer
- }
-}
-
func setupSuite(s *suite.Suite, topologyName string) func() {
t := s.T()
topology, err := LoadTopology(NetworkTopologyDir, topologyName)
diff --git a/extras/hs-test/go.mod b/extras/hs-test/go.mod
index 3b11dd2..0f6bffb 100644
--- a/extras/hs-test/go.mod
+++ b/extras/hs-test/go.mod
@@ -8,6 +8,7 @@
github.com/edwarnicke/govpp v0.0.0-20220311182453-f32f292e0e91
github.com/edwarnicke/vpphelper v0.0.0-20210617172001-3e6797de32c3
github.com/stretchr/testify v1.7.0
+ go.fd.io/govpp v0.7.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -18,13 +19,10 @@
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe // indirect
- github.com/onsi/gomega v1.16.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
- golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
- golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 // indirect
- golang.org/x/text v0.3.7 // indirect
+ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
)
diff --git a/extras/hs-test/go.sum b/extras/hs-test/go.sum
index e08a0d5..531153e 100644
--- a/extras/hs-test/go.sum
+++ b/extras/hs-test/go.sum
@@ -19,21 +19,7 @@
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ftrvxmtrx/fd v0.0.0-20150925145434-c6d800382fff/go.mod h1:yUhRXHewUVJ1k89wHKP68xfzk7kwXUx/DV1nx4EBMbw=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -47,17 +33,9 @@
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe h1:ewr1srjRCmcQogPQ/NCx6XCk6LGVmsVCc9Y3vvPZj+Y=
github.com/lunixbochs/struc v0.0.0-20200521075829-a4cb8d33dbbe/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.1.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
-github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
@@ -73,59 +51,16 @@
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+go.fd.io/govpp v0.7.0 h1:8qideC7G0xPeYz2sjwni8GKWWbNk45Ev73oR1igKDYY=
+go.fd.io/govpp v0.7.0/go.mod h1:VxUPq8HGQH6/9IL9saMURL3UcHsUuN8XmETuao5HA7o=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE=
-golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -134,12 +69,7 @@
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
new file mode 100644
index 0000000..9cf38d9
--- /dev/null
+++ b/extras/hs-test/hst_suite.go
@@ -0,0 +1,289 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/edwarnicke/exechelper"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+ "go.fd.io/govpp/binapi/ip_types"
+ "gopkg.in/yaml.v3"
+)
+
+func IsPersistent() bool {
+ return os.Getenv("HST_PERSIST") == "1"
+}
+
+func IsVerbose() bool {
+ return os.Getenv("HST_VERBOSE") == "1"
+}
+
+type HstSuite struct {
+ suite.Suite
+ teardownSuite func()
+ containers map[string]*Container
+ volumes []string
+ networkNamespaces map[string]*NetworkNamespace
+ veths map[string]*NetworkInterfaceVeth
+ taps map[string]*NetworkInterfaceTap
+ bridges map[string]*NetworkBridge
+ numberOfAddresses int
+}
+
+func (s *HstSuite) TearDownSuite() {
+ if s.teardownSuite != nil {
+ s.teardownSuite() // TODO remove this after config moved to SetupTest() for each suite
+ }
+
+ s.unconfigureNetworkTopology()
+}
+
+func (s *HstSuite) TearDownTest() {
+ if IsPersistent() {
+ return
+ }
+ s.ResetContainers()
+ s.RemoveVolumes()
+}
+
+func (s *HstSuite) SetupTest() {
+ s.SetupVolumes()
+ s.SetupContainers()
+}
+
+func (s *HstSuite) SetupVolumes() {
+ for _, volume := range s.volumes {
+ cmd := "docker volume create --name=" + volume
+ s.log(cmd)
+ exechelper.Run(cmd)
+ }
+}
+
+func (s *HstSuite) SetupContainers() {
+ for _, container := range s.containers {
+ if container.isOptional == false {
+ container.run()
+ }
+ }
+}
+
+func (s *HstSuite) hstFail() {
+ s.T().FailNow()
+}
+
+func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.Nil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotNil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) log(args ...any) {
+ if IsVerbose() {
+ s.T().Log(args...)
+ }
+}
+
+func (s *HstSuite) skip(args ...any) {
+ s.log(args...)
+ s.T().SkipNow()
+}
+
+func (s *HstSuite) ResetContainers() {
+ for _, container := range s.containers {
+ container.stop()
+ }
+}
+
+func (s *HstSuite) RemoveVolumes() {
+ for _, volumeName := range s.volumes {
+ cmd := "docker volume rm " + volumeName
+ exechelper.Run(cmd)
+ os.RemoveAll(volumeName)
+ }
+}
+
+func (s *HstSuite) getContainerByName(name string) *Container {
+ return s.containers[name]
+}
+
+func (s *HstSuite) getContainerCopyByName(name string) *Container {
+ // Create a copy and return its address, so that individial tests which call this
+ // are not able to modify the original container and affect other tests by doing that
+ containerCopy := *s.containers[name]
+ return &containerCopy
+}
+
+func (s *HstSuite) loadContainerTopology(topologyName string) {
+ data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ for _, elem := range yamlTopo.Volumes {
+ volumeMap := elem["volume"].(VolumeConfig)
+ hostDir := volumeMap["host-dir"].(string)
+ s.volumes = append(s.volumes, hostDir)
+ }
+
+ s.containers = make(map[string]*Container)
+ for _, elem := range yamlTopo.Containers {
+ newContainer, err := NewContainer(elem)
+ newContainer.suite = s
+ if err != nil {
+ s.T().Fatalf("container config error: %v", err)
+ }
+ s.log(newContainer.getRunCommand())
+ s.containers[newContainer.name] = newContainer
+ }
+}
+
+func (s *HstSuite) loadNetworkTopology(topologyName string) {
+ data, err := ioutil.ReadFile(NetworkTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ s.networkNamespaces = make(map[string]*NetworkNamespace)
+ s.veths = make(map[string]*NetworkInterfaceVeth)
+ s.taps = make(map[string]*NetworkInterfaceTap)
+ s.bridges = make(map[string]*NetworkBridge)
+ for _, elem := range yamlTopo.Devices {
+ switch elem["type"].(string) {
+ case NetNs:
+ {
+ if namespace, err := NewNetNamespace(elem); err == nil {
+ s.networkNamespaces[namespace.Name()] = &namespace
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Veth:
+ {
+ if veth, err := NewVeth(elem); err == nil {
+ s.veths[veth.Name()] = &veth
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Tap:
+ {
+ if tap, err := NewTap(elem); err == nil {
+ s.taps[tap.Name()] = &tap
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Bridge:
+ {
+ if bridge, err := NewBridge(elem); err == nil {
+ s.bridges[bridge.Name()] = &bridge
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ }
+ }
+}
+
+func (s *HstSuite) configureNetworkTopology(topologyName string) {
+ s.loadNetworkTopology(topologyName)
+
+ for _, ns := range s.networkNamespaces {
+ if err := ns.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, veth := range s.veths {
+ if err := veth.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, tap := range s.taps {
+ if err := tap.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, bridge := range s.bridges {
+ if err := bridge.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+}
+
+func (s *HstSuite) unconfigureNetworkTopology() {
+ if IsPersistent() {
+ return
+ }
+ for _, ns := range s.networkNamespaces {
+ ns.Unconfigure()
+ }
+ for _, veth := range s.veths {
+ veth.Unconfigure()
+ }
+ for _, tap := range s.taps {
+ tap.Unconfigure()
+ }
+ for _, bridge := range s.bridges {
+ bridge.Unconfigure()
+ }
+}
+
+func (s *HstSuite) NewAddress() (AddressWithPrefix, error) {
+ var ipPrefix AddressWithPrefix
+ var err error
+
+ if s.numberOfAddresses == 255 {
+ s.T().Fatalf("no available IPv4 addresses")
+ }
+
+ address := fmt.Sprintf("10.10.10.%v/24", s.numberOfAddresses+1)
+ ipPrefix, err = ip_types.ParseAddressWithPrefix(address)
+ if err != nil {
+ return AddressWithPrefix{}, err
+ }
+ s.numberOfAddresses++
+
+ return ipPrefix, nil
+}
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index 28d27bb..52b7c39 100644
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -31,22 +31,18 @@
serverContainer := s.getContainerByName("server-vpp")
clientContainer := s.getContainerByName("client-vpp")
- _, err := serverContainer.execAction("Configure2Veths srv")
+ serverVeth := s.veths["vppsrv"]
+
+ _, err := serverContainer.vppInstance.vppctl("http cli server")
s.assertNil(err)
- _, err = clientContainer.execAction("Configure2Veths cln")
+ uri := "http://" + serverVeth.Address() + "/80"
+
+ o, err := clientContainer.vppInstance.vppctl("http cli client" +
+ " uri " + uri + " query /show/version")
s.assertNil(err)
- s.log("configured IPs...")
-
- _, err = serverContainer.execAction("RunHttpCliSrv")
- s.assertNil(err)
-
- s.log("configured http server")
-
- o, err := clientContainer.execAction("RunHttpCliCln /show/version")
- s.assertNil(err)
-
+ s.log(o)
s.assertContains(o, "<html>", "<html> not found in the result!")
}
diff --git a/extras/hs-test/ldp_test.go b/extras/hs-test/ldp_test.go
index 2dfdf8b..cbba227 100644
--- a/extras/hs-test/ldp_test.go
+++ b/extras/hs-test/ldp_test.go
@@ -10,12 +10,10 @@
var clnVclConf, srvVclConf Stanza
serverContainer := s.getContainerByName("server-vpp")
- serverVolume := serverContainer.getVolumeByHostDir("/tmp/server")
- srvVcl := serverVolume.containerDir + "/vcl_srv.conf"
+ srvVcl := serverContainer.GetHostWorkDir() + "/vcl_srv.conf"
clientContainer := s.getContainerByName("client-vpp")
- clientVolume := clientContainer.getVolumeByHostDir("/tmp/client")
- clnVcl := clientVolume.containerDir + "/vcl_cln.conf"
+ clnVcl := clientContainer.GetHostWorkDir() + "/vcl_cln.conf"
ldpreload := os.Getenv("HST_LDPRELOAD")
s.assertNotEqual("", ldpreload)
@@ -28,20 +26,14 @@
s.log("starting VPPs")
- originalWorkDir := serverContainer.workDir
- serverContainer.workDir = serverVolume.containerDir
_, err := serverContainer.execAction("Configure2Veths srv")
s.assertNil(err)
- serverContainer.workDir = originalWorkDir
- originalWorkDir = clientContainer.workDir
- clientContainer.workDir = clientVolume.containerDir
_, err = clientContainer.execAction("Configure2Veths cln")
s.assertNil(err)
- clientContainer.workDir = originalWorkDir
clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/2",
- clientVolume.containerDir)
+ clientContainer.GetContainerWorkDir())
err = clnVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
@@ -54,7 +46,7 @@
s.assertNil(err)
serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/1",
- serverVolume.containerDir)
+ serverContainer.GetContainerWorkDir())
err = srvVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
diff --git a/extras/hs-test/netconfig.go b/extras/hs-test/netconfig.go
index 46f23c0..b93e460 100644
--- a/extras/hs-test/netconfig.go
+++ b/extras/hs-test/netconfig.go
@@ -4,22 +4,128 @@
"errors"
"fmt"
"os/exec"
+ "strings"
+
+ "go.fd.io/govpp/binapi/ethernet_types"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip_types"
)
-type NetType string
+type (
+ AddressWithPrefix = ip_types.AddressWithPrefix
+ MacAddress = ethernet_types.MacAddress
+
+ NetConfig struct {
+ Configure func() error
+ Unconfigure func()
+ }
+
+ NetTopology []NetConfig
+
+ NetConfigBase struct {
+ name string
+ category string // what else to call this when `type` is reserved?
+ }
+
+ NetworkInterfaceVeth struct {
+ NetConfigBase
+ index interface_types.InterfaceIndex
+ peerNetworkNamespace string
+ peerName string
+ peerIp4Address string
+ ip4Address ip_types.AddressWithPrefix
+ hwAddress ethernet_types.MacAddress
+ }
+
+ NetworkInterfaceTap struct {
+ NetConfigBase
+ index interface_types.InterfaceIndex
+ ip4Address string
+ }
+
+ NetworkNamespace struct {
+ NetConfigBase
+ }
+
+ NetworkBridge struct {
+ NetConfigBase
+ networkNamespace string
+ interfaces []string
+ }
+)
const (
- NetNs NetType = "netns"
- Veth string = "veth"
- Tap string = "tap"
+ NetNs string = "netns"
+ Veth string = "veth"
+ Tap string = "tap"
+ Bridge string = "bridge"
)
-type NetConfig struct {
- Configure func() error
- Unconfigure func()
+func (b NetConfigBase) Name() string {
+ return b.name
}
-type NetTopology []NetConfig
+func (b NetConfigBase) Type() string {
+ return b.category
+}
+
+func (iface NetworkInterfaceVeth) Configure() error {
+ err := AddVethPair(iface.name, iface.peerName)
+ if err != nil {
+ return err
+ }
+
+ if iface.peerNetworkNamespace != "" {
+ err := LinkSetNetns(iface.peerName, iface.peerNetworkNamespace)
+ if err != nil {
+ return err
+ }
+ }
+
+ if iface.peerIp4Address != "" {
+ err = AddAddress(iface.peerName, iface.peerIp4Address, iface.peerNetworkNamespace)
+ if err != nil {
+ return fmt.Errorf("failed to add configure address for %s: %v", iface.peerName, err)
+ }
+ }
+ return nil
+}
+
+func (iface NetworkInterfaceVeth) Unconfigure() {
+ DelLink(iface.name)
+}
+
+func (iface NetworkInterfaceVeth) Address() string {
+ return strings.Split(iface.ip4Address.String(), "/")[0]
+}
+
+func (iface NetworkInterfaceTap) Configure() error {
+ err := AddTap(iface.name, iface.ip4Address)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (iface NetworkInterfaceTap) Unconfigure() {
+ DelLink(iface.name)
+}
+
+func (ns NetworkNamespace) Configure() error {
+ return addDelNetns(ns.name, true)
+}
+
+func (ns NetworkNamespace) Unconfigure() {
+ addDelNetns(ns.name, false)
+}
+
+func (b NetworkBridge) Configure() error {
+ return AddBridge(b.name, b.interfaces, b.networkNamespace)
+}
+
+func (b NetworkBridge) Unconfigure() {
+ DelBridge(b.name, b.networkNamespace)
+}
func (t *NetTopology) Configure() error {
for _, c := range *t {
@@ -101,6 +207,60 @@
return nc
}
+func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
+ var networkNamespace NetworkNamespace
+ networkNamespace.name = cfg["name"].(string)
+ networkNamespace.category = "netns"
+ return networkNamespace, nil
+}
+
+func NewBridge(cfg NetDevConfig) (NetworkBridge, error) {
+ var bridge NetworkBridge
+ bridge.name = cfg["name"].(string)
+ bridge.category = "bridge"
+ for _, v := range cfg["interfaces"].([]interface{}) {
+ bridge.interfaces = append(bridge.interfaces, v.(string))
+ }
+ bridge.networkNamespace = cfg["netns"].(string)
+ return bridge, nil
+}
+
+func NewVeth(cfg NetDevConfig) (NetworkInterfaceVeth, error) {
+ var veth NetworkInterfaceVeth
+ var err error
+ veth.name = cfg["name"].(string)
+ veth.category = "veth"
+
+ if cfg["preset-hw-address"] != nil {
+ veth.hwAddress, err = ethernet_types.ParseMacAddress(cfg["preset-hw-address"].(string))
+ if err != nil {
+ return NetworkInterfaceVeth{}, err
+ }
+ }
+
+ peer := cfg["peer"].(NetDevConfig)
+
+ veth.peerName = peer["name"].(string)
+
+ if peer["netns"] != nil {
+ veth.peerNetworkNamespace = peer["netns"].(string)
+ }
+
+ if peer["ip4"] != nil {
+ veth.peerIp4Address = peer["ip4"].(string)
+ }
+
+ return veth, nil
+}
+
+func NewTap(cfg NetDevConfig) (NetworkInterfaceTap, error) {
+ var tap NetworkInterfaceTap
+ tap.name = cfg["name"].(string)
+ tap.category = "tap"
+ tap.ip4Address = cfg["ip4"].(string)
+ return tap, nil
+}
+
func DelBridge(brName, ns string) error {
err := SetDevDown(brName, ns)
if err != err {
diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go
index 5276072..81a21a2 100644
--- a/extras/hs-test/suite_veth_test.go
+++ b/extras/hs-test/suite_veth_test.go
@@ -4,12 +4,94 @@
"time"
)
+const (
+ // These correspond to names used in yaml config
+ serverInterfaceName = "vppsrv"
+ clientInterfaceName = "vppcln"
+)
+
type VethsSuite struct {
HstSuite
}
+var ConvertedTests = map[string]any{
+ "TestVeths/TestEchoBuiltin": "",
+ "TestVeths/TestHttpCli": "",
+ "TestVeths/TestVclEchoTcp": "",
+ "TestVeths/TestVclRetryAttach": "",
+}
+
func (s *VethsSuite) SetupSuite() {
time.Sleep(1 * time.Second)
- s.teardownSuite = setupSuite(&s.Suite, "2peerVeth")
+
+ s.configureNetworkTopology("2peerVeth")
+
s.loadContainerTopology("2peerVeth")
}
+
+func (s *VethsSuite) SetupTest() {
+ s.SetupVolumes()
+ s.SetupContainers()
+
+ // TODO remove this after all tests are converted to configuration from test suite
+ if _, ok := ConvertedTests[s.T().Name()]; !ok {
+ return
+ }
+
+ // Setup test conditions
+
+ var startupConfig Stanza
+ startupConfig.
+ NewStanza("session").
+ Append("enable").
+ Append("use-app-socket-api").Close()
+
+ // ... For server
+ serverContainer := s.getContainerByName("server-vpp")
+
+ serverVpp, _ := serverContainer.newVppInstance(startupConfig)
+ s.assertNotNil(serverVpp)
+
+ s.setupServerVpp()
+
+ // ... For client
+ clientContainer := s.getContainerByName("client-vpp")
+
+ clientVpp, _ := clientContainer.newVppInstance(startupConfig)
+ s.assertNotNil(clientVpp)
+
+ s.setupClientVpp()
+}
+
+func (s *VethsSuite) setupServerVpp() {
+ serverVpp := s.getContainerByName("server-vpp").vppInstance
+
+ err := serverVpp.start()
+ s.assertNil(err)
+
+ serverVeth := s.veths["vppsrv"]
+ idx, err := serverVpp.createAfPacket(serverVeth)
+ s.assertNil(err)
+ s.assertNotEqual(0, idx)
+
+ namespaceSecret := "1"
+ err = serverVpp.addAppNamespace(1, idx, namespaceSecret)
+ s.assertNil(err)
+
+}
+
+func (s *VethsSuite) setupClientVpp() {
+ clientVpp := s.getContainerByName("client-vpp").vppInstance
+
+ err := clientVpp.start()
+ s.assertNil(err)
+
+ clientVeth := s.veths["vppcln"]
+ idx, err := clientVpp.createAfPacket(clientVeth)
+ s.assertNil(err)
+ s.assertNotEqual(0, idx)
+
+ clientNamespaceSecret := "2"
+ err = clientVpp.addAppNamespace(2, idx, clientNamespaceSecret)
+ s.assertNil(err)
+}
diff --git a/extras/hs-test/topo-containers/2peerVeth.yaml b/extras/hs-test/topo-containers/2peerVeth.yaml
index 246e5ca..72af1c9 100644
--- a/extras/hs-test/topo-containers/2peerVeth.yaml
+++ b/extras/hs-test/topo-containers/2peerVeth.yaml
@@ -1,33 +1,25 @@
---
volumes:
- volume: &server-vol
- host-dir: server-share
+ host-dir: /tmp/server-share
+ container-dir: /tmp/server-share
+ is-default-work-dir: true
- volume: &client-vol
- host-dir: client-share
+ host-dir: /tmp/client-share
+ container-dir: "/tmp/client-share"
+ is-default-work-dir: true
containers:
- name: "server-vpp"
volumes:
- <<: *server-vol
- container-dir: "/tmp/server-share"
- is-default-work-dir: true
- - host-dir: "/tmp/server"
- container-dir: "/tmp/server"
- name: "client-vpp"
volumes:
- <<: *client-vol
- container-dir: "/tmp/client-share"
- is-default-work-dir: true
- - host-dir: "/tmp/client"
- container-dir: "/tmp/client"
- name: "server-application"
volumes:
- <<: *server-vol
- container-dir: "/tmp/server-share"
- is-default-work-dir: true
- name: "client-application"
volumes:
- <<: *client-vol
- container-dir: "/tmp/client-share"
- is-default-work-dir: true
diff --git a/extras/hs-test/topo-network/2peerVeth.yaml b/extras/hs-test/topo-network/2peerVeth.yaml
index e62e173..9a966dc 100644
--- a/extras/hs-test/topo-network/2peerVeth.yaml
+++ b/extras/hs-test/topo-network/2peerVeth.yaml
@@ -5,6 +5,7 @@
- name: "vppsrv"
type: "veth"
+ preset-hw-address: "00:00:5e:00:53:01"
peer:
name: "vppsrv_veth"
netns: "hsns"
diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go
index 1338498..cf30ece 100644
--- a/extras/hs-test/utils.go
+++ b/extras/hs-test/utils.go
@@ -51,7 +51,7 @@
`
const vclTemplate = `vcl {
- app-socket-api %[1]s
+ app-socket-api %[1]s/var/run/app_ns_sockets/%[2]s
app-scope-global
app-scope-local
namespace-id %[2]s
diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go
index f4273c8..6c809f4 100644
--- a/extras/hs-test/vcl_test.go
+++ b/extras/hs-test/vcl_test.go
@@ -1,6 +1,7 @@
package main
import (
+ "fmt"
"time"
)
@@ -19,73 +20,78 @@
}
func (s *VethsSuite) testVclEcho(proto string) {
- srvVppContainer := s.getContainerByName("server-vpp")
-
- _, err := srvVppContainer.execAction("Configure2Veths srv")
- s.assertNil(err)
-
- clnVppContainer := s.getContainerByName("client-vpp")
-
- _, err = clnVppContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
+ serverVethAddress := s.veths["vppsrv"].Address()
+ uri := proto + "://" + serverVethAddress + "/12344"
echoSrvContainer := s.getContainerByName("server-application")
-
- // run server app
- _, err = echoSrvContainer.execAction("RunEchoServer " + proto)
+ serverCommand := "vpp_echo server TX=RX" +
+ " socket-name " + echoSrvContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/1" +
+ " use-app-socket-api" +
+ " uri " + uri
+ s.log(serverCommand)
+ err := echoSrvContainer.execServer(serverCommand)
s.assertNil(err)
echoClnContainer := s.getContainerByName("client-application")
- o, err := echoClnContainer.execAction("RunEchoClient " + proto)
+ clientCommand := "vpp_echo client" +
+ " socket-name " + echoClnContainer.GetContainerWorkDir() + "/var/run/app_ns_sockets/2" +
+ " use-app-socket-api uri " + uri
+ s.log(clientCommand)
+ o, err := echoClnContainer.exec(clientCommand)
s.assertNil(err)
s.log(o)
}
func (s *VethsSuite) TestVclRetryAttach() {
- s.skip()
+ s.skip("this test takes too long, for now it's being skipped")
s.testRetryAttach("tcp")
}
func (s *VethsSuite) testRetryAttach(proto string) {
- srvVppContainer := s.getContainerByName("server-vpp")
-
- _, err := srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
- s.assertNil(err)
-
- clnVppContainer := s.getContainerByName("client-vpp")
-
- _, err = clnVppContainer.execAction("Configure2Veths cln")
- s.assertNil(err)
+ srvVppContainer := s.getContainerCopyByName("server-vpp")
echoSrvContainer := s.getContainerByName("server-application")
- _, err = echoSrvContainer.execAction("RunVclEchoServer " + proto)
+
+ serverVclConfContent := fmt.Sprintf(vclTemplate, echoSrvContainer.GetContainerWorkDir(), "1")
+ echoSrvContainer.createFile("/vcl.conf", serverVclConfContent)
+
+ echoSrvContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
+ err := echoSrvContainer.execServer("vcl_test_server -p " + proto + " 12346")
s.assertNil(err)
s.log("This whole test case can take around 3 minutes to run. Please be patient.")
s.log("... Running first echo client test, before disconnect.")
- echoClnContainer := s.getContainerByName("client-application")
- _, err = echoClnContainer.execAction("RunVclEchoClient " + proto)
+
+ serverVeth := s.veths[serverInterfaceName]
+ serverVethAddress := serverVeth.Address()
+
+ echoClnContainer := s.getContainerCopyByName("client-application")
+ clientVclConfContent := fmt.Sprintf(vclTemplate, echoClnContainer.GetContainerWorkDir(), "2")
+ echoClnContainer.createFile("/vcl.conf", clientVclConfContent)
+
+ testClientCommand := "vcl_test_client -U -p " + proto + " " + serverVethAddress + " 12346"
+ echoClnContainer.addEnvVar("VCL_CONFIG", "/vcl.conf")
+ o, err := echoClnContainer.exec(testClientCommand)
+ s.log(o)
s.assertNil(err)
s.log("... First test ended. Stopping VPP server now.")
// Stop server-vpp-instance, start it again and then run vcl-test-client once more
+ srvVppContainer.vppInstance.disconnect()
stopVppCommand := "/bin/bash -c 'ps -C vpp_main -o pid= | xargs kill -9'"
_, err = srvVppContainer.exec(stopVppCommand)
s.assertNil(err)
- time.Sleep(5 * time.Second) // Give parent process time to reap the killed child process
- stopVppCommand = "/bin/bash -c 'ps -C hs-test -o pid= | xargs kill -9'"
- _, err = srvVppContainer.exec(stopVppCommand)
- s.assertNil(err)
- _, err = srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
- s.assertNil(err)
+
+ s.setupServerVpp()
s.log("... VPP server is starting again, so waiting for a bit.")
time.Sleep(30 * time.Second) // Wait a moment for the re-attachment to happen
s.log("... Running second echo client test, after disconnect and re-attachment.")
- _, err = echoClnContainer.execAction("RunVclEchoClient " + proto)
+ o, err = echoClnContainer.exec(testClientCommand)
+ s.log(o)
s.assertNil(err)
s.log("Done.")
}
@@ -99,7 +105,8 @@
err := serverVpp.start()
s.assertNil(err, "starting VPP failed")
- _, err = serverVpp.vppctl("test echo server uri tcp://10.10.10.1/20022")
+ serverVeth := s.veths[serverInterfaceName]
+ _, err = serverVpp.vppctl("test echo server uri tcp://%s/20022", serverVeth.Address())
s.assertNil(err, "starting echo server failed")
clientContainer := s.getContainerByName("client-vpp")
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
index 14d6ab6..3f9ea87 100644
--- a/extras/hs-test/vppinstance.go
+++ b/extras/hs-test/vppinstance.go
@@ -4,6 +4,15 @@
"encoding/json"
"fmt"
"github.com/edwarnicke/exechelper"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/binapi/af_packet"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/session"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
)
const vppConfigTemplate = `unix {
@@ -32,27 +41,34 @@
}
plugins {
+ plugin default { disable }
+
plugin unittest_plugin.so { enable }
- plugin dpdk_plugin.so { disable }
- plugin crypto_aesni_plugin.so { enable }
plugin quic_plugin.so { enable }
+ plugin af_packet_plugin.so { enable }
+ plugin hs_apps_plugin.so { enable }
+ plugin http_plugin.so { enable }
}
`
const (
defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
+ defaultApiSocketFilePath = "/var/run/vpp/api.sock"
)
type VppInstance struct {
container *Container
- config VppConfig
+ config *VppConfig
actionFuncName string
+ connection *core.Connection
+ apiChannel api.Channel
}
type VppConfig struct {
Variant string
CliSocketFilePath string
+ additionalConfig Stanza
}
func (vc *VppConfig) getTemplate() string {
@@ -82,10 +98,22 @@
}
func (vpp *VppInstance) getCliSocket() string {
- return fmt.Sprintf("%s%s", vpp.container.workDir, vpp.config.CliSocketFilePath)
+ return fmt.Sprintf("%s%s", vpp.container.GetContainerWorkDir(), vpp.config.CliSocketFilePath)
}
-func (vpp *VppInstance) start() error {
+func (vpp *VppInstance) getRunDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/run/vpp"
+}
+
+func (vpp *VppInstance) getLogDir() string {
+ return vpp.container.GetContainerWorkDir() + "/var/log/vpp"
+}
+
+func (vpp *VppInstance) getEtcDir() string {
+ return vpp.container.GetContainerWorkDir() + "/etc/vpp"
+}
+
+func (vpp *VppInstance) legacyStart() error {
if vpp.actionFuncName == "" {
return fmt.Errorf("vpp start failed: action function name must not be blank")
}
@@ -99,14 +127,70 @@
if err != nil {
return fmt.Errorf("vpp start failed: %s", err)
}
+ return nil
+}
+
+func (vpp *VppInstance) start() error {
+ if vpp.actionFuncName != "" {
+ return vpp.legacyStart()
+ }
+
+ // Create folders
+ containerWorkDir := vpp.container.GetContainerWorkDir()
+
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getRunDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getLogDir())
+ vpp.container.exec("mkdir --mode=0700 -p " + vpp.getEtcDir())
+
+ // Create startup.conf inside the container
+ configContent := fmt.Sprintf(vppConfigTemplate, containerWorkDir, vpp.config.CliSocketFilePath)
+ configContent += vpp.config.additionalConfig.ToString()
+ startupFileName := vpp.getEtcDir() + "/startup.conf"
+ vpp.container.createFile(startupFileName, configContent)
+
+ // Start VPP
+ if err := vpp.container.execServer("vpp -c " + startupFileName); err != nil {
+ return err
+ }
+
+ // Connect to VPP and store the connection
+ sockAddress := vpp.container.GetHostWorkDir() + defaultApiSocketFilePath
+ conn, connEv, err := govpp.AsyncConnect(
+ sockAddress,
+ core.DefaultMaxReconnectAttempts,
+ core.DefaultReconnectInterval)
+ if err != nil {
+ fmt.Println("async connect error: ", err)
+ }
+ vpp.connection = conn
+
+ // ... wait for Connected event
+ e := <-connEv
+ if e.State != core.Connected {
+ fmt.Println("connecting to VPP failed: ", e.Error)
+ }
+
+ // ... check compatibility of used messages
+ ch, err := conn.NewAPIChannel()
+ if err != nil {
+ fmt.Println("creating channel failed: ", err)
+ }
+ if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
+ fmt.Println("compatibility error: ", err)
+ }
+ vpp.apiChannel = ch
return nil
}
-func (vpp *VppInstance) vppctl(command string) (string, error) {
- cliExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
- vpp.container.name, vpp.getCliSocket(), command)
- output, err := exechelper.CombinedOutput(cliExecCommand)
+func (vpp *VppInstance) vppctl(command string, arguments ...any) (string, error) {
+ vppCliCommand := fmt.Sprintf(command, arguments...)
+ containerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
+ vpp.container.name, vpp.getCliSocket(), vppCliCommand)
+ output, err := exechelper.CombinedOutput(containerExecCommand)
if err != nil {
return "", fmt.Errorf("vppctl failed: %s", err)
}
@@ -115,7 +199,7 @@
}
func NewVppInstance(c *Container) *VppInstance {
- var vppConfig VppConfig
+ vppConfig := new(VppConfig)
vppConfig.CliSocketFilePath = defaultCliSocketFilePath
vpp := new(VppInstance)
vpp.container = c
@@ -123,7 +207,7 @@
return vpp
}
-func serializeVppConfig(vppConfig VppConfig) (string, error) {
+func serializeVppConfig(vppConfig *VppConfig) (string, error) {
serializedConfig, err := json.Marshal(vppConfig)
if err != nil {
return "", fmt.Errorf("vpp start failed: serializing configuration failed: %s", err)
@@ -142,3 +226,87 @@
}
return vppConfig, nil
}
+
+func (vpp *VppInstance) createAfPacket(
+ veth *NetworkInterfaceVeth,
+) (interface_types.InterfaceIndex, error) {
+ createReq := &af_packet.AfPacketCreateV2{
+ UseRandomHwAddr: true,
+ HostIfName: veth.Name(),
+ }
+ if veth.hwAddress != (MacAddress{}) {
+ createReq.UseRandomHwAddr = false
+ createReq.HwAddr = veth.hwAddress
+ }
+ createReply := &af_packet.AfPacketCreateV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(createReq).ReceiveReply(createReply); err != nil {
+ return 0, err
+ }
+ veth.index = createReply.SwIfIndex
+
+ // Set to up
+ upReq := &interfaces.SwInterfaceSetFlags{
+ SwIfIndex: veth.index,
+ Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
+ }
+ upReply := &interfaces.SwInterfaceSetFlagsReply{}
+
+ if err := vpp.apiChannel.SendRequest(upReq).ReceiveReply(upReply); err != nil {
+ return 0, err
+ }
+
+ // Add address
+ if veth.ip4Address == (AddressWithPrefix{}) {
+ ipPrefix, err := vpp.container.suite.NewAddress()
+ if err != nil {
+ return 0, err
+ }
+ veth.ip4Address = ipPrefix
+ }
+ addressReq := &interfaces.SwInterfaceAddDelAddress{
+ IsAdd: true,
+ SwIfIndex: veth.index,
+ Prefix: veth.ip4Address,
+ }
+ addressReply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := vpp.apiChannel.SendRequest(addressReq).ReceiveReply(addressReply); err != nil {
+ return 0, err
+ }
+
+ return veth.index, nil
+}
+
+func (vpp *VppInstance) addAppNamespace(
+ secret uint64,
+ ifx interface_types.InterfaceIndex,
+ namespaceId string,
+) error {
+ req := &session.AppNamespaceAddDelV2{
+ Secret: secret,
+ SwIfIndex: ifx,
+ NamespaceID: namespaceId,
+ }
+ reply := &session.AppNamespaceAddDelV2Reply{}
+
+ if err := vpp.apiChannel.SendRequest(req).ReceiveReply(reply); err != nil {
+ return err
+ }
+
+ sessionReq := &session.SessionEnableDisable{
+ IsEnable: true,
+ }
+ sessionReply := &session.SessionEnableDisableReply{}
+
+ if err := vpp.apiChannel.SendRequest(sessionReq).ReceiveReply(sessionReply); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (vpp *VppInstance) disconnect() {
+ vpp.connection.Disconnect()
+ vpp.apiChannel.Close()
+}