diff options
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 } |