From db823ed6e9543741f6969ff160314093002e037e Mon Sep 17 00:00:00 2001 From: Maros Ondrejicka Date: Wed, 14 Dec 2022 16:30:04 +0100 Subject: hs-test: abstract away topology from test cases Definition of shared volumes and containers has been moved to yaml files to be together with network topology. Containers are automatically run at the beginning of each test case and stopped afterward. Type: test Signed-off-by: Maros Ondrejicka Change-Id: I264cbb4f1355f8bd7aade221e9609fb5b9bd693e --- extras/hs-test/Dockerfile.vpp | 2 +- extras/hs-test/README.rst | 101 ++++++++--------- extras/hs-test/actions.go | 47 +++++--- extras/hs-test/container.go | 153 +++++++++++++++++++++++--- extras/hs-test/echo_test.go | 21 +--- extras/hs-test/framework_test.go | 86 ++++++++------- extras/hs-test/http_test.go | 29 ++--- extras/hs-test/ldp_test.go | 40 +++---- extras/hs-test/main.go | 2 +- extras/hs-test/proxy_test.go | 79 +++++-------- extras/hs-test/suite_ns_test.go | 11 ++ extras/hs-test/suite_tap_test.go | 15 +++ extras/hs-test/suite_veth_test.go | 16 +++ extras/hs-test/topo-containers/2peerVeth.yaml | 31 ++++++ extras/hs-test/topo-containers/ns.yaml | 27 +++++ extras/hs-test/topo-network/2peerVeth.yaml | 24 ++++ extras/hs-test/topo-network/ns.yaml | 21 ++++ extras/hs-test/topo-network/tap.yaml | 5 + extras/hs-test/topo.go | 3 + extras/hs-test/topo/2peerVeth.yaml | 23 ---- extras/hs-test/topo/ns.yaml | 21 ---- extras/hs-test/topo/tap.yaml | 5 - extras/hs-test/utils.go | 97 +--------------- extras/hs-test/vcl_test.go | 103 ++++------------- extras/hs-test/vppinstance.go | 47 +++++--- 25 files changed, 536 insertions(+), 473 deletions(-) create mode 100644 extras/hs-test/suite_ns_test.go create mode 100644 extras/hs-test/suite_tap_test.go create mode 100644 extras/hs-test/suite_veth_test.go create mode 100755 extras/hs-test/topo-containers/2peerVeth.yaml create mode 100755 extras/hs-test/topo-containers/ns.yaml create mode 100755 extras/hs-test/topo-network/2peerVeth.yaml create mode 100755 extras/hs-test/topo-network/ns.yaml create mode 100755 extras/hs-test/topo-network/tap.yaml delete mode 100755 extras/hs-test/topo/2peerVeth.yaml delete mode 100755 extras/hs-test/topo/ns.yaml delete mode 100755 extras/hs-test/topo/tap.yaml (limited to 'extras') 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 ) }()`` 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, "", " 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-network/2peerVeth.yaml b/extras/hs-test/topo-network/2peerVeth.yaml new file mode 100755 index 00000000000..e62e1739a15 --- /dev/null +++ b/extras/hs-test/topo-network/2peerVeth.yaml @@ -0,0 +1,24 @@ +--- +devices: + - name: "hsns" + type: "netns" + + - name: "vppsrv" + type: "veth" + peer: + name: "vppsrv_veth" + netns: "hsns" + + - name: "vppcln" + type: "veth" + peer: + name: "vppcln_veth" + netns: "hsns" + + - name: "br" + type: "bridge" + netns: "hsns" + interfaces: + - vppsrv_veth + - vppcln_veth + diff --git a/extras/hs-test/topo-network/ns.yaml b/extras/hs-test/topo-network/ns.yaml new file mode 100755 index 00000000000..2f7ed39d2e2 --- /dev/null +++ b/extras/hs-test/topo-network/ns.yaml @@ -0,0 +1,21 @@ +--- +devices: + - name: "client" + type: "netns" + + - name: "server" + type: "netns" + + - name: "vpp0" + type: "veth" + peer: + name: "client" + netns: "client" + ip4: "10.0.0.1/24" + + - name: "vpp1" + type: "veth" + peer: + name: "server" + netns: "server" + ip4: "10.0.1.1/24" diff --git a/extras/hs-test/topo-network/tap.yaml b/extras/hs-test/topo-network/tap.yaml new file mode 100755 index 00000000000..4cd95d6e48a --- /dev/null +++ b/extras/hs-test/topo-network/tap.yaml @@ -0,0 +1,5 @@ +--- +devices: + - name: "tap0" + type: "tap" + ip4: "10.10.10.1/24" 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/topo/2peerVeth.yaml b/extras/hs-test/topo/2peerVeth.yaml deleted file mode 100755 index d40f3803879..00000000000 --- a/extras/hs-test/topo/2peerVeth.yaml +++ /dev/null @@ -1,23 +0,0 @@ ---- -devices: - - name: "hsns" - type: "netns" - - - name: "vppsrv" - type: "veth" - peer: - name: "vppsrv_veth" - netns: "hsns" - - - name: "vppcln" - type: "veth" - peer: - name: "vppcln_veth" - netns: "hsns" - - - name: "br" - type: "bridge" - netns: "hsns" - interfaces: - - vppsrv_veth - - vppcln_veth diff --git a/extras/hs-test/topo/ns.yaml b/extras/hs-test/topo/ns.yaml deleted file mode 100755 index c1c8c540b2b..00000000000 --- a/extras/hs-test/topo/ns.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -devices: - - name: "client" - type: "netns" - - - name: "server" - type: "netns" - - - name: "vpp0" - type: "veth" - peer: - name: "client" - netns: "client" - ip4: "10.0.0.1/24" - - - name: "vpp1" - type: "veth" - peer: - name: "server" - netns: "server" - ip4: "10.0.1.1/24" \ No newline at end of file diff --git a/extras/hs-test/topo/tap.yaml b/extras/hs-test/topo/tap.yaml deleted file mode 100755 index 4cd95d6e48a..00000000000 --- a/extras/hs-test/topo/tap.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -devices: - - name: "tap0" - type: "tap" - ip4: "10.10.10.1/24" 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 } -- cgit 1.2.3-korg