summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xextras/hs-test/Dockerfile.vpp2
-rwxr-xr-xextras/hs-test/README.rst101
-rwxr-xr-xextras/hs-test/actions.go47
-rw-r--r--extras/hs-test/container.go153
-rwxr-xr-xextras/hs-test/echo_test.go21
-rwxr-xr-xextras/hs-test/framework_test.go86
-rwxr-xr-xextras/hs-test/http_test.go29
-rwxr-xr-xextras/hs-test/ldp_test.go40
-rwxr-xr-xextras/hs-test/main.go2
-rwxr-xr-xextras/hs-test/proxy_test.go79
-rw-r--r--extras/hs-test/suite_ns_test.go11
-rw-r--r--extras/hs-test/suite_tap_test.go15
-rw-r--r--extras/hs-test/suite_veth_test.go16
-rwxr-xr-xextras/hs-test/topo-containers/2peerVeth.yaml31
-rwxr-xr-xextras/hs-test/topo-containers/ns.yaml27
-rwxr-xr-xextras/hs-test/topo-network/2peerVeth.yaml (renamed from extras/hs-test/topo/2peerVeth.yaml)1
-rwxr-xr-xextras/hs-test/topo-network/ns.yaml (renamed from extras/hs-test/topo/ns.yaml)2
-rwxr-xr-xextras/hs-test/topo-network/tap.yaml (renamed from extras/hs-test/topo/tap.yaml)0
-rwxr-xr-xextras/hs-test/topo.go3
-rwxr-xr-xextras/hs-test/utils.go97
-rwxr-xr-xextras/hs-test/vcl_test.go103
-rw-r--r--extras/hs-test/vppinstance.go47
22 files changed, 488 insertions, 425 deletions
diff --git a/extras/hs-test/Dockerfile.vpp b/extras/hs-test/Dockerfile.vpp
index 92577870b42..e72c581c142 100755
--- a/extras/hs-test/Dockerfile.vpp
+++ b/extras/hs-test/Dockerfile.vpp
@@ -12,7 +12,7 @@ COPY vpp-data/lib/vpp_plugins/ /usr/lib/x86_64-linux-gnu/vpp_plugins/
COPY vpp-data/bin/* /usr/bin/
COPY vpp-data/lib/* /usr/lib/
-COPY hs-test /hs-test
+COPY hs-test /usr/local/bin/hs-test
RUN addgroup vpp
diff --git a/extras/hs-test/README.rst b/extras/hs-test/README.rst
index 7a99621c8dc..47d3a53ecbf 100755
--- a/extras/hs-test/README.rst
+++ b/extras/hs-test/README.rst
@@ -44,19 +44,18 @@ For adding a new suite, please see `Modifying the framework`_ below.
#. Declare method whose name starts with ``Test`` and specifies its receiver as a pointer to the suite's struct (defined in ``framework_test.go``)
#. Implement test behaviour inside the test method. This typically includes the following:
- #. Start docker container(s) as needed. Function ``dockerRun(instance, args string)``
- from ``utils.go`` serves this purpose. Alternatively use suite struct's ``NewContainer(name string)`` method to create
+ #. Retrieve a running container in which to run some action. Function ``getContainerByName(name string)``
+ from ``HstSuite`` struct serves this purpose
an object representing a container and start it with ``run()`` method
#. Execute *hs-test* action(s) inside any of the running containers.
- Function ``hstExec`` from ``utils.go`` does this by using ``docker exec`` command to run ``hs-test`` executable.
- For starting an VPP instance inside a container, the ``VppInstance`` struct can be used as a forward-looking alternative
- #. Run arbitrary commands inside the containers with ``dockerExec(cmd string, instance string)``
+ Function ``execAction(args string)`` from ``container.go`` does this by using ``docker exec`` command to run ``hs-test`` executable.
+ For starting an VPP instance inside a container, the ``VppInstance`` struct can be used instead
+ #. Run arbitrary commands inside the containers with ``exec(cmd string)``
#. Run other external tool with one of the preexisting functions in the ``utils.go`` file.
For example, use ``wget`` with ``startWget(..)`` function
#. Use ``exechelper`` or just plain ``exec`` packages to run whatever else
- #. ``defer func() { exechelper.Run("docker stop <container-name>) }()`` inside the method body,
- to stop the running container(s). It's not necessary to do this if containers were created
- with suite's ``NewContainer(..)`` method
+ #. Verify results of your tests using ``assert`` methods provided by the test suite,
+ implemented by HstSuite struct
**Example test case**
@@ -69,51 +68,24 @@ This can be put in file ``extras/hs-test/my_test.go`` and run with command ``./t
import (
"fmt"
- "github.com/edwarnicke/exechelper"
)
func (s *MySuite) TestMyCase() {
- t := s.T()
+ serverVppContainer := s.getContainerByName("server-vpp")
- vpp1Instance := "vpp-1"
- vpp2Instance := "vpp-2"
+ serverVpp := NewVppInstance(serverContainer)
+ serverVpp.set2VethsServer()
+ serverVpp.start()
- err := dockerRun(vpp1Instance, "")
- if err != nil {
- t.Errorf("%v", err)
- return
- }
- defer func() { exechelper.Run("docker stop " + vpp1Instance) }()
-
- err = dockerRun(vpp2Instance, "")
- if err != nil {
- t.Errorf("%v", err)
- return
- }
- defer func() { exechelper.Run("docker stop " + vpp2Instance) }()
-
- _, err = hstExec("Configure2Veths srv", vpp1Instance)
- if err != nil {
- t.Errorf("%v", err)
- return
- }
+ clientVppContainer := s.getContainerByName("client-vpp")
- _, err = hstExec("Configure2Veths cln", vpp2Instance)
- if err != nil {
- t.Errorf("%v", err)
- return
- }
+ clientVpp:= NewVppInstance(clientContainer)
+ serverVpp.set2VethsClient()
+ clientVpp.start()
- // ping one VPP from the other
- //
- // not using dockerExec because it executes in detached mode
- // and we want to capture output from ping and show it
- command := "docker exec --detach=false vpp-1 vppctl -s /tmp/2veths/var/run/vpp/cli.sock ping 10.10.10.2"
- output, err := exechelper.CombinedOutput(command)
- if err != nil {
- t.Errorf("ping failed: %v", err)
- }
- fmt.Println(string(output))
+ result, err := clientVpp.vppctl("ping 10.10.10.2")
+ s.assertNil(err, "ping resulted in error")
+ fmt.Println(result)
}
Modifying the framework
@@ -123,7 +95,9 @@ Modifying the framework
.. _test-convention:
-#. Adding a new suite takes place in ``framework_test.go``
+#. Adding a new suite takes place in ``framework_test.go`` and by creating a new file for the suite.
+ Naming convention for the suite files is ``suite-name-test.go`` where *name* will be replaced
+ by the actual name
#. Make a ``struct`` with at least ``HstSuite`` struct as its member.
HstSuite provides functionality that can be shared for all suites, like starting containers
@@ -136,8 +110,12 @@ Modifying the framework
#. Implement SetupSuite method which testify runs before running the tests.
It's important here to call ``setupSuite(s *suite.Suite, topologyName string)`` and assign its result to the suite's ``teardownSuite`` member.
- Pass the topology name to the function in the form of file name of one of the *yaml* files in ``topo`` folder.
- Without the extension. In this example, *myTopology* corresponds to file ``extras/hs-test/topo/myTopology.yaml``
+ Pass the topology name to the function in the form of file name of one of the *yaml* files in ``topo-network`` folder.
+ Without the extension. In this example, *myTopology* corresponds to file ``extras/hs-test/topo-network/myTopology.yaml``
+ This will ensure network topology, such as network interfaces and namespaces, will be created.
+ Another important method to call is ``loadContainerTopology(topologyName string)`` which will load
+ containers and shared volumes used by the suite. This time the name passed to method corresponds
+ to file in ``extras/hs-test/topo-containers`` folder
::
@@ -145,9 +123,11 @@ Modifying the framework
// Add custom setup code here
s.teardownSuite = setupSuite(&s.Suite, "myTopology")
+ s.loadContainerTopology("2peerVeth")
}
-#. In order for ``go test`` to run this suite, we need to create a normal test function and pass our suite to ``suite.Run``
+#. In order for ``go test`` to run this suite, we need to create a normal test function and pass our suite to ``suite.Run``.
+ This is being at the end of ``framework_test.go``
::
@@ -160,20 +140,27 @@ Modifying the framework
**Adding a topology element**
-Topology configuration exists as ``yaml`` files in the ``extras/hs-test/topo`` folder.
-Processing of a file for a particular test suite is started by the ``setupSuite`` function depending on which file's name is passed to it.
+Topology configuration exists as ``yaml`` files in the ``extras/hs-test/topo-network`` and
+``extras/hs-test/topo-containers`` folders. Processing of a network topology file for a particular test suite
+is started by the ``setupSuite`` function depending on which file's name is passed to it.
Specified file is loaded by ``LoadTopology()`` function and converted into internal data structures which represent various elements of the topology.
After parsing the configuration, ``Configure()`` method loops over array of topology elements and configures them one by one.
-These are currently supported types of elements.
+These are currently supported types of network elements.
* ``netns`` - network namespace
* ``veth`` - veth network interface, optionally with target network namespace or IPv4 address
* ``bridge`` - ethernet bridge to connect created interfaces, optionally with target network namespace
* ``tap`` - tap network interface with IP address
+Similarly, container topology is started by ``loadContainerTopology()``, configuration file is processed
+so that test suite retains map of defined containers and uses that to start them at the beginning
+of each test case and stop containers after the test finishes. Container configuration can specify
+also volumes which allow to share data between containers or between host system and containers.
+
Supporting a new type of topology element requires adding code to recognize the new element type during loading.
-And adding code to set up the element in the host system with some Linux tool, such as *ip*. This should be implemented in ``netconfig.go``.
+And adding code to set up the element in the host system with some Linux tool, such as *ip*.
+This should be implemented in ``netconfig.go`` for network and in ``container.go`` for containers and volumes.
**Communicating between containers**
@@ -193,13 +180,13 @@ For example, starting up VPP or running VCL echo client.
The actions are located in ``extras/hs-test/actions.go``. To add one, create a new method that has its receiver as a pointer to ``Actions`` struct.
-Run it from test case with ``hstExec(args, instance)`` where ``args`` is the action method's name and ``instance`` is target Docker container's name.
+Run it from test case with container's method ``execAction(args)`` where ``args`` is the action method's name.
This then executes the ``hs-test`` binary inside of the container and it then runs selected action.
Action is specified by its name as first argument for the binary.
-*Note*: When ``hstExec(..)`` runs some action from a test case, the execution of ``hs-test`` inside the container
+*Note*: When ``execAction(args)`` runs some action from a test case, the execution of ``hs-test`` inside the container
is asynchronous. The action might take many seconds to finish, while the test case execution context continues to run.
-To mitigate this, ``hstExec(..)`` waits pre-defined arbitrary number of seconds for a *sync file* to be written by ``hs-test``
+To mitigate this, ``execAction(args)`` waits pre-defined arbitrary number of seconds for a *sync file* to be written by ``hs-test``
at the end of its run. The test case context and container use Docker volume to share the file.
**Adding an external tool**
diff --git a/extras/hs-test/actions.go b/extras/hs-test/actions.go
index fe07b5fdb39..34096df27ba 100755
--- a/extras/hs-test/actions.go
+++ b/extras/hs-test/actions.go
@@ -19,6 +19,10 @@ import (
"github.com/edwarnicke/vpphelper"
)
+var (
+ workDir, _ = os.Getwd()
+)
+
type ConfFn func(context.Context, api.Connection) error
type Actions struct {
@@ -44,20 +48,22 @@ func configureProxyTcp(ifName0, ipAddr0, ifName1, ipAddr1 string) ConfFn {
func (a *Actions) RunHttpCliSrv(args []string) *ActionResult {
cmd := fmt.Sprintf("http cli server")
- return ApiCliInband("/tmp/Configure2Veths", cmd)
+ 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("/tmp/Configure2Veths", cmd)
+ return ApiCliInband(workDir, cmd)
}
func (a *Actions) ConfigureVppProxy(args []string) *ActionResult {
ctx, cancel := newVppContext()
defer cancel()
- con, vppErrCh := vpphelper.StartAndDialContext(ctx, vpphelper.WithVppConfig(configTemplate))
+ con, vppErrCh := vpphelper.StartAndDialContext(ctx,
+ vpphelper.WithVppConfig(configTemplate),
+ vpphelper.WithRootDir(workDir))
exitOnErrCh(ctx, cancel, vppErrCh)
confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24")
@@ -83,7 +89,7 @@ func (a *Actions) ConfigureEnvoyProxy(args []string) *ActionResult {
con, vppErrCh := vpphelper.StartAndDialContext(ctx,
vpphelper.WithVppConfig(configTemplate+startup.ToString()),
- vpphelper.WithRootDir("/tmp/vpp-envoy"))
+ vpphelper.WithRootDir(workDir))
exitOnErrCh(ctx, cancel, vppErrCh)
confFn := configureProxyTcp("vpp0", "10.0.0.2/24", "vpp1", "10.0.1.2/24")
@@ -91,7 +97,7 @@ func (a *Actions) ConfigureEnvoyProxy(args []string) *ActionResult {
if err != nil {
return NewActionResult(err, ActionResultWithDesc("configuration failed"))
}
- err0 := exechelper.Run("chmod 777 -R /tmp/vpp-envoy")
+ err0 := exechelper.Run("chmod 777 -R " + workDir)
if err0 != nil {
return NewActionResult(err, ActionResultWithDesc("setting permissions failed"))
}
@@ -120,7 +126,7 @@ func (a *Actions) RunEchoClient(args []string) *ActionResult {
outBuff := bytes.NewBuffer([]byte{})
errBuff := bytes.NewBuffer([]byte{})
- cmd := fmt.Sprintf("vpp_echo client socket-name /tmp/echo-cln/var/run/app_ns_sockets/2 use-app-socket-api uri %s://10.10.10.1/12344", args[2])
+ 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))
@@ -130,7 +136,7 @@ func (a *Actions) RunEchoClient(args []string) *ActionResult {
}
func (a *Actions) RunEchoServer(args []string) *ActionResult {
- cmd := fmt.Sprintf("vpp_echo server TX=RX socket-name /tmp/echo-srv/var/run/app_ns_sockets/1 use-app-socket-api uri %s://10.10.10.1/12344", args[2])
+ 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:
@@ -143,12 +149,12 @@ func (a *Actions) RunEchoServer(args []string) *ActionResult {
func (a *Actions) RunEchoSrvInternal(args []string) *ActionResult {
cmd := fmt.Sprintf("test echo server %s uri tcp://10.10.10.1/1234", getArgs())
- return ApiCliInband("/tmp/Configure2Veths", cmd)
+ return ApiCliInband(workDir, cmd)
}
func (a *Actions) RunEchoClnInternal(args []string) *ActionResult {
cmd := fmt.Sprintf("test echo client %s uri tcp://10.10.10.1/1234", getArgs())
- return ApiCliInband("/tmp/Configure2Veths", cmd)
+ return ApiCliInband(workDir, cmd)
}
func (a *Actions) RunVclEchoServer(args []string) *ActionResult {
@@ -156,10 +162,11 @@ func (a *Actions) RunVclEchoServer(args []string) *ActionResult {
if err != nil {
return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
}
- fmt.Fprintf(f, vclTemplate, "/tmp/echo-srv/var/run/app_ns_sockets/1", "1")
+ 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")
+ os.Setenv("VCL_CONFIG", "./vcl_1.conf")
cmd := fmt.Sprintf("vcl_test_server -p %s 12346", args[2])
errCh := exechelper.Start(cmd)
select {
@@ -179,10 +186,11 @@ func (a *Actions) RunVclEchoClient(args []string) *ActionResult {
if err != nil {
return NewActionResult(err, ActionResultWithStderr(("create vcl config: ")))
}
- fmt.Fprintf(f, vclTemplate, "/tmp/echo-cln/var/run/app_ns_sockets/2", "2")
+ 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")
+ 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),
@@ -237,22 +245,25 @@ func (a *Actions) Configure2Veths(args []string) *ActionResult {
ctx, cancel := newVppContext()
defer cancel()
- vppConfig, err := DeserializeVppConfig(args[2])
+ vppConfig, err := deserializeVppConfig(args[2])
if err != nil {
return NewActionResult(err, ActionResultWithDesc("deserializing configuration failed"))
}
con, vppErrCh := vpphelper.StartAndDialContext(ctx,
vpphelper.WithVppConfig(vppConfig.getTemplate()+startup.ToString()),
- vpphelper.WithRootDir(fmt.Sprintf("/tmp/%s", args[1])))
+ vpphelper.WithRootDir(workDir))
exitOnErrCh(ctx, cancel, vppErrCh)
var fn func(context.Context, api.Connection) error
- if vppConfig.Variant == "srv" {
+ switch vppConfig.Variant {
+ case "srv":
fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1)
- } else if vppConfig.Variant == "srv-with-preset-hw-addr" {
+ case "srv-with-preset-hw-addr":
fn = configure2vethsTopo("vppsrv", "10.10.10.1/24", "1", 1, "00:00:5e:00:53:01")
- } else {
+ case "cln":
+ fallthrough
+ default:
fn = configure2vethsTopo("vppcln", "10.10.10.2/24", "2", 2)
}
err = fn(ctx, con)
diff --git a/extras/hs-test/container.go b/extras/hs-test/container.go
index e9aa7b261c4..1dc65fff308 100644
--- a/extras/hs-test/container.go
+++ b/extras/hs-test/container.go
@@ -2,18 +2,73 @@ package main
import (
"fmt"
+ "os"
+ "strings"
"github.com/edwarnicke/exechelper"
)
type Volume struct {
- name string
- path string
+ hostDir string
+ containerDir string
}
type Container struct {
- name string
- volumes []*Volume
+ isOptional bool
+ name string
+ image string
+ workDir string
+ volumes map[string]Volume
+ envVars map[string]string
+}
+
+func NewContainer(yamlInput ContainerConfig) (*Container, error) {
+ containerName := yamlInput["name"].(string)
+ if len(containerName) == 0 {
+ err := fmt.Errorf("container name must not be blank")
+ return nil, err
+ }
+
+ var container = new(Container)
+ container.volumes = make(map[string]Volume)
+ container.envVars = make(map[string]string)
+ container.name = containerName
+
+ if image, ok := yamlInput["image"]; ok {
+ container.image = image.(string)
+ } else {
+ container.image = "hs-test/vpp"
+ }
+
+ if isOptional, ok := yamlInput["is-optional"]; ok {
+ container.isOptional = isOptional.(bool)
+ } else {
+ container.isOptional = false
+ }
+
+ if _, ok := yamlInput["volumes"]; ok {
+ r:= strings.NewReplacer("$HST_DIR", workDir)
+ for _, volu := range yamlInput["volumes"].([]interface{}) {
+ volumeMap := volu.(ContainerConfig)
+ hostDir := r.Replace(volumeMap["host-dir"].(string))
+ containerDir := volumeMap["container-dir"].(string)
+ container.addVolume(hostDir, containerDir)
+
+ if isDefaultWorkDir, ok := volumeMap["is-default-work-dir"]; ok &&
+ isDefaultWorkDir.(bool) &&
+ len(container.workDir) == 0 {
+ container.workDir = containerDir
+ }
+
+ }
+ }
+
+ if _, ok := yamlInput["vars"]; ok {
+ for _, envVar := range yamlInput["vars"].([]interface{}) {
+ container.addEnvVar(envVar)
+ }
+ }
+ return container, nil
}
func (c *Container) run() error {
@@ -22,34 +77,102 @@ func (c *Container) run() error {
}
exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", c.name))
- syncPath := fmt.Sprintf("-v /tmp/%s/sync:/tmp/sync", c.name)
- cmd := "docker run --cap-add=all -d --privileged --network host --rm "
+ syncPath := fmt.Sprintf(" -v %s:/tmp/sync", c.getSyncPath())
+ cmd := "docker run --cap-add=all -d --privileged --network host --rm"
cmd += syncPath
- cmd += c.getVolumes()
- cmd += " --name " + c.name + " hs-test/vpp"
+ cmd += c.getVolumesAsCliOption()
+ cmd += c.getEnvVarsAsCliOption()
+ cmd += " --name " + c.name + " " + c.image
fmt.Println(cmd)
err := exechelper.Run(cmd)
if err != nil {
- return fmt.Errorf("create volume failed: %s", err)
+ return fmt.Errorf("container run failed: %s", err)
}
return nil
}
-func (c *Container) addVolume(name string, containerPath string) {
- c.volumes = append(c.volumes, &Volume{name, containerPath})
+func (c *Container) addVolume(hostDir string, containerDir string) {
+ var volume Volume
+ volume.hostDir = hostDir
+ volume.containerDir = containerDir
+ c.volumes[hostDir] = volume
}
-func (c *Container) getVolumes() string {
- dockerOption := ""
+func (c *Container) getVolumeByHostDir(hostDir string) Volume {
+ return c.volumes[hostDir]
+}
+
+func (c *Container) getVolumesAsCliOption() string {
+ cliOption := ""
if len(c.volumes) > 0 {
for _, volume := range c.volumes {
- dockerOption += fmt.Sprintf(" -v %s:%s", volume.name, volume.path)
+ cliOption += fmt.Sprintf(" -v %s:%s", volume.hostDir, volume.containerDir)
}
}
- return dockerOption
+ return cliOption
+}
+
+func (c *Container) getWorkDirAsCliOption() string {
+ if len(c.workDir) == 0 {
+ return ""
+ }
+ return fmt.Sprintf(" --workdir=\"%s\"", c.workDir)
+}
+
+func (c *Container) addEnvVar(envVar interface{}) {
+ envVarMap := envVar.(ContainerConfig)
+ name := envVarMap["name"].(string)
+ value := envVarMap["value"].(string)
+ c.envVars[name] = value
+}
+
+func (c *Container) getEnvVarsAsCliOption() string {
+ cliOption := ""
+ if len(c.envVars) == 0 {
+ return cliOption
+ }
+
+ for name, value := range c.envVars {
+ cliOption += fmt.Sprintf(" -e %s=%s", name, value)
+ }
+ return cliOption
+}
+
+func (c *Container) getSyncPath() string {
+ return fmt.Sprintf("/tmp/%s/sync", c.name)
+}
+
+func (c *Container) exec(command string) (string, error) {
+ cliCommand := "docker exec -d " + c.name + " " + command
+ byteOutput, err := exechelper.CombinedOutput(cliCommand)
+ return string(byteOutput), err
+}
+
+func (c *Container) execAction(args string) (string, error) {
+ syncFile := c.getSyncPath() + "/rc"
+ os.Remove(syncFile)
+
+ workDir := c.getWorkDirAsCliOption()
+ cmd := fmt.Sprintf("docker exec -d %s %s hs-test %s",
+ workDir,
+ c.name,
+ args)
+ err := exechelper.Run(cmd)
+ if err != nil {
+ return "", err
+ }
+ res, err := waitForSyncFile(syncFile)
+ if err != nil {
+ return "", fmt.Errorf("failed to read sync file while executing 'hs-test %s': %v", args, err)
+ }
+ o := res.StdOutput + res.ErrOutput
+ if res.Code != 0 {
+ return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc)
+ }
+ return o, err
}
func (c *Container) stop() error {
diff --git a/extras/hs-test/echo_test.go b/extras/hs-test/echo_test.go
index 9f91e2afeb7..9bc8f76105c 100755
--- a/extras/hs-test/echo_test.go
+++ b/extras/hs-test/echo_test.go
@@ -2,30 +2,21 @@ package main
import (
"fmt"
-
- "github.com/edwarnicke/exechelper"
)
func (s *VethsSuite) TestEchoBuiltin() {
- srvInstance := "echo-srv-internal"
- clnInstance := "echo-cln-internal"
-
- s.assertNil(dockerRun(srvInstance, ""), "failed to start docker (srv)")
- defer func() { exechelper.Run("docker stop " + srvInstance) }()
-
- s.assertNil(dockerRun(clnInstance, ""), "failed to start docker (cln)")
- defer func() { exechelper.Run("docker stop " + clnInstance) }()
-
- _, err := hstExec("Configure2Veths srv", srvInstance)
+ serverContainer := s.getContainerByName("server-vpp")
+ _, err := serverContainer.execAction("Configure2Veths srv")
s.assertNil(err)
- _, err = hstExec("Configure2Veths cln", clnInstance)
+ clientContainer := s.getContainerByName("client-vpp")
+ _, err = clientContainer.execAction("Configure2Veths cln")
s.assertNil(err)
- _, err = hstExec("RunEchoSrvInternal private-segment-size 1g fifo-size 4 no-echo", srvInstance)
+ _, err = serverContainer.execAction("RunEchoSrvInternal private-segment-size 1g fifo-size 4 no-echo")
s.assertNil(err)
- o, err := hstExec("RunEchoClnInternal nclients 10000 bytes 1 syn-timeout 100 test-timeout 100 no-return private-segment-size 1g fifo-size 4", clnInstance)
+ o, err := clientContainer.execAction("RunEchoClnInternal nclients 10000 bytes 1 syn-timeout 100 test-timeout 100 no-return private-segment-size 1g fifo-size 4")
s.assertNil(err)
fmt.Println(o)
}
diff --git a/extras/hs-test/framework_test.go b/extras/hs-test/framework_test.go
index 3df509f30c1..a3d46ad0ab9 100755
--- a/extras/hs-test/framework_test.go
+++ b/extras/hs-test/framework_test.go
@@ -3,26 +3,43 @@ package main
import (
"fmt"
"testing"
- "time"
+ "io/ioutil"
"github.com/edwarnicke/exechelper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
+ "gopkg.in/yaml.v3"
)
type HstSuite struct {
suite.Suite
teardownSuite func()
- containers []*Container
+ containers map[string]*Container
volumes []string
}
func (s *HstSuite) TearDownSuite() {
s.teardownSuite()
- s.StopContainers()
+}
+
+func (s *HstSuite) TearDownTest() {
+ s.ResetContainers()
s.RemoveVolumes()
}
+func (s *HstSuite) SetupTest() {
+ for _, volume := range s.volumes {
+ cmd := "docker volume create --name=" + volume
+ fmt.Println(cmd)
+ exechelper.Run(cmd)
+ }
+ for _, container := range s.containers {
+ if container.isOptional == false {
+ container.run()
+ }
+ }
+}
+
func (s *HstSuite) hstFail() {
s.T().FailNow()
}
@@ -63,20 +80,7 @@ func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArg
}
}
-func (s *HstSuite) NewContainer(name string) (*Container, error) {
- if name == "" {
- return nil, fmt.Errorf("creating container failed: name must not be blank")
- }
-
- container := new(Container)
- container.name = name
-
- s.containers = append(s.containers, container)
-
- return container, nil
-}
-
-func (s *HstSuite) StopContainers() {
+func (s *HstSuite) ResetContainers() {
for _, container := range s.containers {
container.stop()
}
@@ -94,39 +98,43 @@ func (s *HstSuite) NewVolume(name string) error {
func (s *HstSuite) RemoveVolumes() {
for _, volumeName := range s.volumes {
- exechelper.Run("docker volume rm " + volumeName)
+ cmd := "docker volume rm " + volumeName
+ exechelper.Run(cmd)
}
}
-type TapSuite struct {
- HstSuite
+func (s *HstSuite) getContainerByName(name string) *Container {
+ return s.containers[name]
}
-func (s *TapSuite) SetupSuite() {
- time.Sleep(1 * time.Second)
- s.teardownSuite = setupSuite(&s.Suite, "tap")
-}
-
-type VethsSuite struct {
- HstSuite
-}
-
-func (s *VethsSuite) SetupSuite() {
- time.Sleep(1 * time.Second)
- s.teardownSuite = setupSuite(&s.Suite, "2peerVeth")
-}
+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)
+ }
-type NsSuite struct {
- HstSuite
-}
+ for _, elem := range yamlTopo.Volumes {
+ s.volumes = append(s.volumes, elem)
+ }
-func (s *NsSuite) SetupSuite() {
- s.teardownSuite = setupSuite(&s.Suite, "ns")
+ 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.containers[newContainer.name] = newContainer
+ }
}
func setupSuite(s *suite.Suite, topologyName string) func() {
t := s.T()
- topology, err := LoadTopology(TopologyDir, topologyName)
+ topology, err := LoadTopology(NetworkTopologyDir, topologyName)
if err != nil {
t.Fatalf("error on loading topology '%s': %v", topologyName, err)
}
diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go
index d2fb5a3c85a..1cbddec9e76 100755
--- a/extras/hs-test/http_test.go
+++ b/extras/hs-test/http_test.go
@@ -1,23 +1,17 @@
package main
-import (
- "github.com/edwarnicke/exechelper"
-)
-
func (s *NsSuite) TestHttpTps() {
t := s.T()
finished := make(chan error, 1)
server_ip := "10.0.0.2"
port := "8080"
- dockerInstance := "http-tps"
- t.Log("starting vpp..")
+ container := s.getContainerByName("vpp")
- s.assertNil(dockerRun(dockerInstance, ""), "failed to start docker")
- defer func() { exechelper.Run("docker stop " + dockerInstance) }()
+ t.Log("starting vpp..")
// start & configure vpp in the container
- _, err := hstExec("ConfigureHttpTps", dockerInstance)
+ _, err := container.execAction("ConfigureHttpTps")
s.assertNil(err)
go startWget(finished, server_ip, port, "client")
@@ -29,28 +23,23 @@ func (s *NsSuite) TestHttpTps() {
func (s *VethsSuite) TestHttpCli() {
t := s.T()
- srvInstance := "http-cli-srv"
- clnInstance := "http-cli-cln"
- s.assertNil(dockerRun(srvInstance, ""), "failed to start docker (srv)")
- defer func() { exechelper.Run("docker stop " + srvInstance) }()
-
- s.assertNil(dockerRun(clnInstance, ""), "failed to start docker (cln)")
- defer func() { exechelper.Run("docker stop " + clnInstance) }()
+ serverContainer := s.getContainerByName("server-vpp")
+ clientContainer := s.getContainerByName("client-vpp")
- _, err := hstExec("Configure2Veths srv", srvInstance)
+ _, err := serverContainer.execAction("Configure2Veths srv")
s.assertNil(err)
- _, err = hstExec("Configure2Veths cln", clnInstance)
+ _, err = clientContainer.execAction("Configure2Veths cln")
s.assertNil(err)
t.Log("configured IPs...")
- _, err = hstExec("RunHttpCliSrv", srvInstance)
+ _, err = serverContainer.execAction("RunHttpCliSrv")
s.assertNil(err)
t.Log("configured http server")
- o, err := hstExec("RunHttpCliCln /show/version", clnInstance)
+ o, err := clientContainer.execAction("RunHttpCliCln /show/version")
s.assertNil(err)
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 683c6a37525..9a8b8121e8a 100755
--- a/extras/hs-test/ldp_test.go
+++ b/extras/hs-test/ldp_test.go
@@ -4,22 +4,18 @@ import (
"fmt"
"os"
"time"
-
- "github.com/edwarnicke/exechelper"
)
func (s *VethsSuite) TestLDPreloadIperfVpp() {
var clnVclConf, srvVclConf Stanza
- srvInstance := "vpp-ldp-srv"
- clnInstance := "vpp-ldp-cln"
- srvPath := "/tmp/" + srvInstance
- clnPath := "/tmp/" + clnInstance
- srvVcl := srvPath + "/vcl_srv.conf"
- clnVcl := clnPath + "/vcl_cln.conf"
+ serverContainer := s.getContainerByName("server-vpp")
+ serverVolume := serverContainer.getVolumeByHostDir("/tmp/server")
+ srvVcl := serverVolume.containerDir + "/vcl_srv.conf"
- exechelper.Run("mkdir " + srvPath)
- exechelper.Run("mkdir " + clnPath)
+ clientContainer := s.getContainerByName("client-vpp")
+ clientVolume := clientContainer.getVolumeByHostDir("/tmp/client")
+ clnVcl := clientVolume.containerDir + "/vcl_cln.conf"
ldpreload := os.Getenv("HST_LDPRELOAD")
s.assertNotEqual("", ldpreload)
@@ -32,18 +28,20 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
fmt.Println("starting VPPs")
- s.assertNil(dockerRun(srvInstance, fmt.Sprintf("-v /tmp/%s:/tmp", srvInstance)), "failed to start docker (srv)")
- defer func() { exechelper.Run("docker stop " + srvInstance) }()
-
- s.assertNil(dockerRun(clnInstance, fmt.Sprintf("-v /tmp/%s:/tmp", clnInstance)), "failed to start docker (cln)")
- defer func() { exechelper.Run("docker stop " + clnInstance) }()
-
- _, err := hstExec("Configure2Veths srv", srvInstance)
+ originalWorkDir := serverContainer.workDir
+ serverContainer.workDir = serverVolume.containerDir
+ _, err := serverContainer.execAction("Configure2Veths srv")
s.assertNil(err)
+ serverContainer.workDir = originalWorkDir
- _, err = hstExec("Configure2Veths cln", clnInstance)
+ 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)
err = clnVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
@@ -51,10 +49,12 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
- Append(fmt.Sprintf("app-socket-api /tmp/%s/Configure2Veths/var/run/app_ns_sockets/2", clnInstance)).Close().
+ Append(clientAppSocketApi).Close().
SaveToFile(clnVcl)
s.assertNil(err)
+ serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/1",
+ serverVolume.containerDir)
err = srvVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
@@ -62,7 +62,7 @@ func (s *VethsSuite) TestLDPreloadIperfVpp() {
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
- Append(fmt.Sprintf("app-socket-api /tmp/%s/Configure2Veths/var/run/app_ns_sockets/1", srvInstance)).Close().
+ Append(serverAppSocketApi).Close().
SaveToFile(srvVcl)
s.assertNil(err)
diff --git a/extras/hs-test/main.go b/extras/hs-test/main.go
index 9de0d314cb2..98627a53e12 100755
--- a/extras/hs-test/main.go
+++ b/extras/hs-test/main.go
@@ -133,7 +133,7 @@ func main() {
}
if os.Args[1] == "rm" {
- topology, err := LoadTopology(TopologyDir, os.Args[2])
+ topology, err := LoadTopology(NetworkTopologyDir, os.Args[2])
if err != nil {
fmt.Printf("falied to load topologies: %v\n", err)
os.Exit(1)
diff --git a/extras/hs-test/proxy_test.go b/extras/hs-test/proxy_test.go
index 0ada6fad0f7..952bcc5aa76 100755
--- a/extras/hs-test/proxy_test.go
+++ b/extras/hs-test/proxy_test.go
@@ -1,34 +1,22 @@
package main
import (
- "context"
"fmt"
"os"
"github.com/edwarnicke/exechelper"
)
-func testProxyHttpTcp(s *NsSuite, dockerInstance, action string, proxySetup func() error) error {
+func testProxyHttpTcp(s *NsSuite, proxySetup func() error) error {
const outputFile = "test.data"
const srcFile = "10M"
stopServer := make(chan struct{}, 1)
serverRunning := make(chan struct{}, 1)
- volumeArgs := fmt.Sprintf("-v shared-vol:/tmp/%s", dockerInstance)
- s.assertNil(dockerRun(dockerInstance, volumeArgs), "failed to start container")
- defer func() { exechelper.Run("docker stop " + dockerInstance) }()
-
- // start & configure vpp in the container
- _, err := hstExec(action, dockerInstance)
- s.assertNil(err)
-
- fmt.Println("VPP running and configured...")
-
s.assertNil(proxySetup(), "failed to setup proxy")
- fmt.Println("Proxy configured...")
// create test file
- err = exechelper.Run(fmt.Sprintf("ip netns exec server truncate -s %s %s", srcFile, srcFile))
+ err := exechelper.Run(fmt.Sprintf("ip netns exec server truncate -s %s %s", srcFile, srcFile))
s.assertNil(err, "failed to run truncate command")
defer func() { os.Remove(srcFile) }()
@@ -55,50 +43,43 @@ func testProxyHttpTcp(s *NsSuite, dockerInstance, action string, proxySetup func
return nil
}
-func setupEnvoy(ctx context.Context, dockerInstance string) error {
- errCh := startEnvoy(ctx, dockerInstance)
- select {
- case err := <-errCh:
- return err
- default:
- }
-
- go func(ctx context.Context, errCh <-chan error) {
- for {
- select {
- // handle cancel() call from outside to gracefully stop the routine
- case <-ctx.Done():
- return
- default:
- select {
- case err := <-errCh:
- fmt.Printf("error while running envoy: %v", err)
- default:
- }
- }
- }
- }(ctx, errCh)
+func configureVppProxy(s *NsSuite) error {
+ container := s.getContainerByName("vpp")
+ testVppProxy := NewVppInstance(container)
+ testVppProxy.setVppProxy()
+ err := testVppProxy.start()
+ s.assertNil(err, "failed to start and configure VPP")
+ fmt.Println("VPP running and configured...")
+
+ output, err := testVppProxy.vppctl("test proxy server server-uri tcp://10.0.0.2/555 client-uri tcp://10.0.1.1/666")
+ fmt.Println("Proxy configured...", string(output))
return nil
}
func (s *NsSuite) TestVppProxyHttpTcp() {
- dockerInstance := "vpp-proxy"
- err := testProxyHttpTcp(s, dockerInstance, "ConfigureVppProxy", configureVppProxy)
+ err := testProxyHttpTcp(s, func() error {
+ return configureVppProxy(s)
+ })
s.assertNil(err)
}
-func (s *NsSuite) TestEnvoyProxyHttpTcp() {
- exechelper.Run("docker volume create --name=shared-vol")
- defer func() {
- exechelper.Run("docker stop envoy")
- }()
+func configureEnvoyProxy(s *NsSuite) error {
+ vppContainer := s.getContainerByName("vpp")
+ testVppForEnvoyProxy := NewVppInstance(vppContainer)
+ testVppForEnvoyProxy.setEnvoyProxy()
+ err := testVppForEnvoyProxy.start()
+ s.assertNil(err, "failed to start and configure VPP")
- ctx, cancel := context.WithCancel(context.Background())
+ envoyContainer := s.getContainerByName("envoy")
+ envoyContainer.run()
- dockerInstance := "vpp-envoy"
- err := testProxyHttpTcp(s, dockerInstance, "ConfigureEnvoyProxy", func() error {
- return setupEnvoy(ctx, dockerInstance)
+ fmt.Println("VPP running and configured...")
+ return nil
+}
+
+func (s *NsSuite) TestEnvoyProxyHttpTcp() {
+ err := testProxyHttpTcp(s, func() error {
+ return configureEnvoyProxy(s)
})
s.assertNil(err)
- cancel()
}
diff --git a/extras/hs-test/suite_ns_test.go b/extras/hs-test/suite_ns_test.go
new file mode 100644
index 00000000000..d45f6cc0132
--- /dev/null
+++ b/extras/hs-test/suite_ns_test.go
@@ -0,0 +1,11 @@
+package main
+
+type NsSuite struct {
+ HstSuite
+}
+
+func (s *NsSuite) SetupSuite() {
+ s.teardownSuite = setupSuite(&s.Suite, "ns")
+ s.loadContainerTopology("ns")
+}
+
diff --git a/extras/hs-test/suite_tap_test.go b/extras/hs-test/suite_tap_test.go
new file mode 100644
index 00000000000..ffcd8b72a70
--- /dev/null
+++ b/extras/hs-test/suite_tap_test.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ "time"
+)
+
+type TapSuite struct {
+ HstSuite
+}
+
+func (s *TapSuite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.teardownSuite = setupSuite(&s.Suite, "tap")
+}
+
diff --git a/extras/hs-test/suite_veth_test.go b/extras/hs-test/suite_veth_test.go
new file mode 100644
index 00000000000..f41e87df128
--- /dev/null
+++ b/extras/hs-test/suite_veth_test.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+ "time"
+)
+
+type VethsSuite struct {
+ HstSuite
+}
+
+func (s *VethsSuite) SetupSuite() {
+ time.Sleep(1 * time.Second)
+ s.teardownSuite = setupSuite(&s.Suite, "2peerVeth")
+ s.loadContainerTopology("2peerVeth")
+}
+
diff --git a/extras/hs-test/topo-containers/2peerVeth.yaml b/extras/hs-test/topo-containers/2peerVeth.yaml
new file mode 100755
index 00000000000..8262232eff8
--- /dev/null
+++ b/extras/hs-test/topo-containers/2peerVeth.yaml
@@ -0,0 +1,31 @@
+---
+volumes:
+ - server-share
+ - client-share
+
+containers:
+ - name: "server-vpp"
+ volumes:
+ - host-dir: "server-share"
+ container-dir: "/tmp/server-share"
+ is-default-work-dir: true
+ - host-dir: "/tmp/server"
+ container-dir: "/tmp/server"
+ - name: "client-vpp"
+ volumes:
+ - host-dir: "client-share"
+ container-dir: "/tmp/client-share"
+ is-default-work-dir: true
+ - host-dir: "/tmp/client"
+ container-dir: "/tmp/client"
+ - name: "server-application"
+ volumes:
+ - host-dir: "server-share"
+ container-dir: "/tmp/server-share"
+ is-default-work-dir: true
+ - name: "client-application"
+ volumes:
+ - host-dir: "client-share"
+ container-dir: "/tmp/client-share"
+ is-default-work-dir: true
+
diff --git a/extras/hs-test/topo-containers/ns.yaml b/extras/hs-test/topo-containers/ns.yaml
new file mode 100755
index 00000000000..2cb4fdc80f4
--- /dev/null
+++ b/extras/hs-test/topo-containers/ns.yaml
@@ -0,0 +1,27 @@
+---
+volumes:
+ - shared-vol
+
+# $HST_DIR will be replaced during runtime by path to hs-test directory
+containers:
+ - name: "vpp"
+ volumes:
+ - host-dir: "shared-vol"
+ container-dir: "/tmp/vpp"
+ is-default-work-dir: true
+ - name: "envoy"
+ volumes:
+ - host-dir: "$HST_DIR/envoy/proxy.yaml"
+ container-dir: "/etc/envoy/envoy.yaml"
+ - host-dir: "shared-vol"
+ container-dir: "/tmp/vpp-envoy"
+ is-default-work-dir: true
+ - host-dir: "$HST_DIR/envoy"
+ container-dir: "/tmp"
+ vars:
+ - name: "ENVOY_UID"
+ value: "0"
+ - name: "VCL_CONFIG"
+ value: "/tmp/vcl.conf"
+ image: "envoyproxy/envoy-contrib:v1.21-latest"
+ is-optional: true
diff --git a/extras/hs-test/topo/2peerVeth.yaml b/extras/hs-test/topo-network/2peerVeth.yaml
index d40f3803879..e62e1739a15 100755
--- a/extras/hs-test/topo/2peerVeth.yaml
+++ b/extras/hs-test/topo-network/2peerVeth.yaml
@@ -21,3 +21,4 @@ devices:
interfaces:
- vppsrv_veth
- vppcln_veth
+
diff --git a/extras/hs-test/topo/ns.yaml b/extras/hs-test/topo-network/ns.yaml
index c1c8c540b2b..2f7ed39d2e2 100755
--- a/extras/hs-test/topo/ns.yaml
+++ b/extras/hs-test/topo-network/ns.yaml
@@ -18,4 +18,4 @@ devices:
peer:
name: "server"
netns: "server"
- ip4: "10.0.1.1/24" \ No newline at end of file
+ ip4: "10.0.1.1/24"
diff --git a/extras/hs-test/topo/tap.yaml b/extras/hs-test/topo-network/tap.yaml
index 4cd95d6e48a..4cd95d6e48a 100755
--- a/extras/hs-test/topo/tap.yaml
+++ b/extras/hs-test/topo-network/tap.yaml
diff --git a/extras/hs-test/topo.go b/extras/hs-test/topo.go
index f11761460d8..1d501e8f2da 100755
--- a/extras/hs-test/topo.go
+++ b/extras/hs-test/topo.go
@@ -10,9 +10,12 @@ import (
)
type NetDevConfig map[string]interface{}
+type ContainerConfig map[string]interface{}
type YamlTopology struct {
Devices []NetDevConfig `yaml:"devices"`
+ Containers []ContainerConfig `yaml:"containers"`
+ Volumes []string `yaml:"volumes"`
}
func AddAddress(device, address, ns string) error {
diff --git a/extras/hs-test/utils.go b/extras/hs-test/utils.go
index 581b8461082..bec54e8cf50 100755
--- a/extras/hs-test/utils.go
+++ b/extras/hs-test/utils.go
@@ -1,7 +1,6 @@
package main
import (
- "context"
"encoding/json"
"errors"
"fmt"
@@ -11,8 +10,6 @@ import (
"os/exec"
"strings"
"time"
-
- "github.com/edwarnicke/exechelper"
)
// TODO remove `configTemplate` once its usage has been replaced everywhere with VppConfig
@@ -61,7 +58,8 @@ const vclTemplate = `vcl {
}
`
-const TopologyDir string = "topo/"
+const NetworkTopologyDir string = "topo-network/"
+const ContainerTopologyDir string = "topo-containers/"
type Stanza struct {
content string
@@ -126,30 +124,6 @@ func StartClientApp(env []string, clnCh chan error) {
}
}
-// run vpphelper in docker
-func hstExec(args string, instance string) (string, error) {
- syncFile := fmt.Sprintf("/tmp/%s/sync/rc", instance)
- os.Remove(syncFile)
-
- c := "docker exec -d " + instance + " /hs-test " + args
- err := exechelper.Run(c)
- if err != nil {
- return "", err
- }
-
- res, err := waitForSyncFile(syncFile)
-
- if err != nil {
- return "", fmt.Errorf("failed to read sync file while executing './hs-test %s': %v", args, err)
- }
-
- o := res.StdOutput + res.ErrOutput
- if res.Code != 0 {
- return o, fmt.Errorf("cmd resulted in non-zero value %d: %s", res.Code, res.Desc)
- }
- return o, err
-}
-
func waitForSyncFile(fname string) (*JsonResult, error) {
var res JsonResult
@@ -173,17 +147,6 @@ func waitForSyncFile(fname string) (*JsonResult, error) {
return nil, fmt.Errorf("no sync file found")
}
-func dockerRun(instance, args string) error {
- exechelper.Run(fmt.Sprintf("mkdir -p /tmp/%s/sync", instance))
- syncPath := fmt.Sprintf("-v /tmp/%s/sync:/tmp/sync", instance)
- cmd := "docker run --cap-add=all -d --privileged --network host --rm "
- cmd += syncPath
- cmd += " " + args
- cmd += " --name " + instance + " hs-test/vpp"
- fmt.Println(cmd)
- return exechelper.Run(cmd)
-}
-
func assertFileSize(f1, f2 string) error {
fi1, err := os.Stat(f1)
if err != nil {
@@ -201,62 +164,6 @@ func assertFileSize(f1, f2 string) error {
return nil
}
-func dockerExec(cmd string, instance string) ([]byte, error) {
- c := "docker exec -d " + instance + " " + cmd
- return exechelper.CombinedOutput(c)
-}
-
-func startEnvoy(ctx context.Context, dockerInstance string) <-chan error {
- errCh := make(chan error)
- wd, err := os.Getwd()
- if err != nil {
- errCh <- err
- return errCh
- }
-
- c := []string{"docker", "run", "--rm", "--name", "envoy",
- "-v", fmt.Sprintf("%s/envoy/proxy.yaml:/etc/envoy/envoy.yaml", wd),
- "-v", fmt.Sprintf("shared-vol:/tmp/%s", dockerInstance),
- "-v", fmt.Sprintf("%s/envoy:/tmp", wd),
- "-e", "ENVOY_UID=0",
- "-e", "VCL_CONFIG=/tmp/vcl.conf",
- "envoyproxy/envoy-contrib:v1.21-latest"}
- fmt.Println(c)
-
- go func(errCh chan error) {
- count := 0
- var cmd *exec.Cmd
- for ; ; count++ {
- cmd = NewCommand(c, "")
- err = cmd.Start()
- if err == nil {
- break
- }
- if count > 5 {
- errCh <- fmt.Errorf("failed to start envoy docker after %d attempts", count)
- return
- }
- }
-
- err = cmd.Wait()
- if err != nil {
- errCh <- fmt.Errorf("failed to start docker: %v", err)
- return
- }
- <-ctx.Done()
- }(errCh)
- return errCh
-}
-
-func configureVppProxy() error {
- _, err := dockerExec("vppctl test proxy server server-uri tcp://10.0.0.2/555 client-uri tcp://10.0.1.1/666",
- "vpp-proxy")
- if err != nil {
- return fmt.Errorf("error while configuring vpp proxy test: %v", err)
- }
- return nil
-}
-
func startHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
cmd := NewCommand([]string{"./http_server", addressPort}, netNs)
err := cmd.Start()
diff --git a/extras/hs-test/vcl_test.go b/extras/hs-test/vcl_test.go
index 8fd64f1dcd6..80775542712 100755
--- a/extras/hs-test/vcl_test.go
+++ b/extras/hs-test/vcl_test.go
@@ -3,7 +3,6 @@ package main
import (
"fmt"
"time"
-
)
func (s *VethsSuite) TestVclEchoQuic() {
@@ -21,47 +20,25 @@ func (s *VethsSuite) TestVclEchoTcp() {
}
func (s *VethsSuite) testVclEcho(proto string) {
- serverVolume := "echo-srv-vol"
- s.NewVolume(serverVolume)
-
- clientVolume := "echo-cln-vol"
- s.NewVolume(clientVolume)
+ srvVppContainer := s.getContainerByName("server-vpp")
- srvInstance := "vpp-vcl-test-srv"
- serverVppContainer, err := s.NewContainer(srvInstance)
+ _, err := srvVppContainer.execAction("Configure2Veths srv")
s.assertNil(err)
- serverVppContainer.addVolume(serverVolume, "/tmp/Configure2Veths")
- s.assertNil(serverVppContainer.run())
- clnInstance := "vpp-vcl-test-cln"
- clientVppContainer, err := s.NewContainer(clnInstance)
- s.assertNil(err)
- clientVppContainer.addVolume(clientVolume, "/tmp/Configure2Veths")
- s.assertNil(clientVppContainer.run())
+ clnVppContainer := s.getContainerByName("client-vpp")
- echoSrv := "echo-srv"
- serverEchoContainer, err := s.NewContainer(echoSrv)
+ _, err = clnVppContainer.execAction("Configure2Veths cln")
s.assertNil(err)
- serverEchoContainer.addVolume(serverVolume, "/tmp/" + echoSrv)
- s.assertNil(serverEchoContainer.run())
- echoCln := "echo-cln"
- clientEchoContainer, err := s.NewContainer(echoCln)
- s.assertNil(err)
- clientEchoContainer.addVolume(clientVolume, "/tmp/" + echoCln)
- s.assertNil(clientEchoContainer.run())
-
- _, err = hstExec("Configure2Veths srv", srvInstance)
- s.assertNil(err)
-
- _, err = hstExec("Configure2Veths cln", clnInstance)
- s.assertNil(err)
+ echoSrvContainer := s.getContainerByName("server-application")
// run server app
- _, err = hstExec("RunEchoServer "+proto, echoSrv)
+ _, err = echoSrvContainer.execAction("RunEchoServer "+proto)
s.assertNil(err)
- o, err := hstExec("RunEchoClient "+proto, echoCln)
+ echoClnContainer := s.getContainerByName("client-application")
+
+ o, err := echoClnContainer.execAction("RunEchoClient "+proto)
s.assertNil(err)
fmt.Println(o)
@@ -73,95 +50,63 @@ func (s *VethsSuite) TestVclRetryAttach() {
}
func (s *VethsSuite) testRetryAttach(proto string) {
- serverVolume := "echo-srv-vol"
- s.NewVolume(serverVolume)
+ srvVppContainer := s.getContainerByName("server-vpp")
- clientVolume := "echo-cln-vol"
- s.NewVolume(clientVolume)
-
- srvInstance := "vpp-vcl-test-srv"
- serverVppContainer, err := s.NewContainer(srvInstance)
+ _, err := srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
s.assertNil(err)
- serverVppContainer.addVolume(serverVolume, "/tmp/Configure2Veths")
- s.assertNil(serverVppContainer.run())
- clnInstance := "vpp-vcl-test-cln"
- clientVppContainer, err := s.NewContainer(clnInstance)
- s.assertNil(err)
- clientVppContainer.addVolume(clientVolume, "/tmp/Configure2Veths")
- s.assertNil(clientVppContainer.run())
+ clnVppContainer := s.getContainerByName("client-vpp")
- echoSrv := "echo-srv"
- serverEchoContainer, err := s.NewContainer(echoSrv)
+ _, err = clnVppContainer.execAction("Configure2Veths cln")
s.assertNil(err)
- serverEchoContainer.addVolume(serverVolume, "/tmp/" + echoSrv)
- s.assertNil(serverEchoContainer.run())
- echoCln := "echo-cln"
- clientEchoContainer, err := s.NewContainer(echoCln)
- s.assertNil(err)
- clientEchoContainer.addVolume(clientVolume, "/tmp/" + echoCln)
- s.assertNil(clientEchoContainer.run())
-
- _, err = hstExec("Configure2Veths srv-with-preset-hw-addr", srvInstance)
- s.assertNil(err)
-
- _, err = hstExec("Configure2Veths cln", clnInstance)
- s.assertNil(err)
-
- _, err = hstExec("RunVclEchoServer "+proto, echoSrv)
+ echoSrvContainer := s.getContainerByName("server-application")
+ _, err = echoSrvContainer.execAction("RunVclEchoServer "+proto)
s.assertNil(err)
fmt.Println("This whole test case can take around 3 minutes to run. Please be patient.")
fmt.Println("... Running first echo client test, before disconnect.")
- _, err = hstExec("RunVclEchoClient "+proto, echoCln)
+ echoClnContainer := s.getContainerByName("client-application")
+ _, err = echoClnContainer.execAction("RunVclEchoClient "+proto)
s.assertNil(err)
fmt.Println("... First test ended. Stopping VPP server now.")
// Stop server-vpp-instance, start it again and then run vcl-test-client once more
stopVppCommand := "/bin/bash -c 'ps -C vpp_main -o pid= | xargs kill -9'"
- _, err = dockerExec(stopVppCommand, srvInstance)
+ _, 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 = dockerExec(stopVppCommand, srvInstance)
+ _, err = srvVppContainer.exec(stopVppCommand)
s.assertNil(err)
- _, err = hstExec("Configure2Veths srv-with-preset-hw-addr", srvInstance)
+ _, err = srvVppContainer.execAction("Configure2Veths srv-with-preset-hw-addr")
s.assertNil(err)
fmt.Println("... VPP server is starting again, so waiting for a bit.")
time.Sleep(30 * time.Second) // Wait a moment for the re-attachment to happen
fmt.Println("... Running second echo client test, after disconnect and re-attachment.")
- _, err = hstExec("RunVclEchoClient "+proto, echoCln)
+ _, err = echoClnContainer.execAction("RunVclEchoClient "+proto)
s.assertNil(err)
fmt.Println("Done.")
}
func (s *VethsSuite) TestTcpWithLoss() {
- serverContainer, err := s.NewContainer("server")
- s.assertNil(err, "creating container failed")
- err = serverContainer.run()
- s.assertNil(err)
+ serverContainer := s.getContainerByName("server-vpp")
serverVpp := NewVppInstance(serverContainer)
s.assertNotNil(serverVpp)
- serverVpp.setCliSocket("/var/run/vpp/cli.sock")
serverVpp.set2VethsServer()
- err = serverVpp.start()
+ err := serverVpp.start()
s.assertNil(err, "starting VPP failed")
_, err = serverVpp.vppctl("test echo server uri tcp://10.10.10.1/20022")
s.assertNil(err, "starting echo server failed")
- clientContainer, err := s.NewContainer("client")
- s.assertNil(err, "creating container failed")
- err = clientContainer.run()
- s.assertNil(err, "starting container failed")
+ clientContainer := s.getContainerByName("client-vpp")
clientVpp := NewVppInstance(clientContainer)
s.assertNotNil(clientVpp)
- clientVpp.setCliSocket("/var/run/vpp/cli.sock")
clientVpp.set2VethsClient()
err = clientVpp.start()
s.assertNil(err, "starting VPP failed")
diff --git a/extras/hs-test/vppinstance.go b/extras/hs-test/vppinstance.go
index c6d3935cc60..6194ce88106 100644
--- a/extras/hs-test/vppinstance.go
+++ b/extras/hs-test/vppinstance.go
@@ -40,6 +40,10 @@ plugins {
`
+const (
+ defaultCliSocketFilePath = "/var/run/vpp/cli.sock"
+)
+
type VppInstance struct {
container *Container
config VppConfig
@@ -65,28 +69,30 @@ func (vpp *VppInstance) set2VethsClient() {
vpp.config.Variant = "cln"
}
+func (vpp *VppInstance) setVppProxy() {
+ vpp.actionFuncName = "ConfigureVppProxy"
+}
+
+func (vpp *VppInstance) setEnvoyProxy() {
+ vpp.actionFuncName = "ConfigureEnvoyProxy"
+}
+
func (vpp *VppInstance) setCliSocket(filePath string) {
vpp.config.CliSocketFilePath = filePath
}
func (vpp *VppInstance) getCliSocket() string {
- return fmt.Sprintf("/tmp/%s/%s", vpp.actionFuncName, vpp.config.CliSocketFilePath)
+ return fmt.Sprintf("%s%s", vpp.container.workDir, vpp.config.CliSocketFilePath)
}
func (vpp *VppInstance) start() error {
- if vpp.config.Variant == "" {
- return fmt.Errorf("vpp start failed: variant must not be blank")
- }
if vpp.actionFuncName == "" {
return fmt.Errorf("vpp start failed: action function name must not be blank")
}
- serializedConfig, err := json.Marshal(vpp.config)
- if err != nil {
- return fmt.Errorf("vpp start failed: serializing configuration failed: %s", err)
- }
- args := fmt.Sprintf("%s '%s'", vpp.actionFuncName, string(serializedConfig))
- _, err = hstExec(args, vpp.container.name)
+ serializedConfig, err := serializeVppConfig(vpp.config)
+ args := fmt.Sprintf("%s '%s'", vpp.actionFuncName, serializedConfig)
+ _, err = vpp.container.execAction(args)
if err != nil {
return fmt.Errorf("vpp start failed: %s", err)
}
@@ -95,9 +101,9 @@ func (vpp *VppInstance) start() error {
}
func (vpp *VppInstance) vppctl(command string) (string, error) {
- dockerExecCommand := fmt.Sprintf("docker exec --detach=false %[1]s vppctl -s %[2]s %[3]s",
+ 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(dockerExecCommand)
+ output, err := exechelper.CombinedOutput(cliExecCommand)
if err != nil {
return "", fmt.Errorf("vppctl failed: %s", err)
}
@@ -106,19 +112,30 @@ func (vpp *VppInstance) vppctl(command string) (string, error) {
}
func NewVppInstance(c *Container) *VppInstance {
+ var vppConfig VppConfig
+ vppConfig.CliSocketFilePath = defaultCliSocketFilePath
vpp := new(VppInstance)
vpp.container = c
+ vpp.config = vppConfig
return vpp
}
-func DeserializeVppConfig(input string) (VppConfig, 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)
+ }
+ return string(serializedConfig), nil
+}
+
+func deserializeVppConfig(input string) (VppConfig, error) {
var vppConfig VppConfig
err := json.Unmarshal([]byte(input), &vppConfig)
if err != nil {
- // Since input is not a valid JSON it is going be used as variant value
+ // Since input is not a valid JSON it is going be used as a variant value
// for compatibility reasons
vppConfig.Variant = input
- vppConfig.CliSocketFilePath = "/var/run/vpp/cli.sock"
+ vppConfig.CliSocketFilePath = defaultCliSocketFilePath
}
return vppConfig, nil
}