summaryrefslogtreecommitdiffstats
path: root/extras/hs-test/hst_suite.go
diff options
context:
space:
mode:
Diffstat (limited to 'extras/hs-test/hst_suite.go')
-rw-r--r--extras/hs-test/hst_suite.go289
1 files changed, 289 insertions, 0 deletions
diff --git a/extras/hs-test/hst_suite.go b/extras/hs-test/hst_suite.go
new file mode 100644
index 00000000000..9cf38d95895
--- /dev/null
+++ b/extras/hs-test/hst_suite.go
@@ -0,0 +1,289 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "github.com/edwarnicke/exechelper"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+ "go.fd.io/govpp/binapi/ip_types"
+ "gopkg.in/yaml.v3"
+)
+
+func IsPersistent() bool {
+ return os.Getenv("HST_PERSIST") == "1"
+}
+
+func IsVerbose() bool {
+ return os.Getenv("HST_VERBOSE") == "1"
+}
+
+type HstSuite struct {
+ suite.Suite
+ teardownSuite func()
+ containers map[string]*Container
+ volumes []string
+ networkNamespaces map[string]*NetworkNamespace
+ veths map[string]*NetworkInterfaceVeth
+ taps map[string]*NetworkInterfaceTap
+ bridges map[string]*NetworkBridge
+ numberOfAddresses int
+}
+
+func (s *HstSuite) TearDownSuite() {
+ if s.teardownSuite != nil {
+ s.teardownSuite() // TODO remove this after config moved to SetupTest() for each suite
+ }
+
+ s.unconfigureNetworkTopology()
+}
+
+func (s *HstSuite) TearDownTest() {
+ if IsPersistent() {
+ return
+ }
+ s.ResetContainers()
+ s.RemoveVolumes()
+}
+
+func (s *HstSuite) SetupTest() {
+ s.SetupVolumes()
+ s.SetupContainers()
+}
+
+func (s *HstSuite) SetupVolumes() {
+ for _, volume := range s.volumes {
+ cmd := "docker volume create --name=" + volume
+ s.log(cmd)
+ exechelper.Run(cmd)
+ }
+}
+
+func (s *HstSuite) SetupContainers() {
+ for _, container := range s.containers {
+ if container.isOptional == false {
+ container.run()
+ }
+ }
+}
+
+func (s *HstSuite) hstFail() {
+ s.T().FailNow()
+}
+
+func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.Nil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotNil(s.T(), object, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
+ if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
+ s.hstFail()
+ }
+}
+
+func (s *HstSuite) log(args ...any) {
+ if IsVerbose() {
+ s.T().Log(args...)
+ }
+}
+
+func (s *HstSuite) skip(args ...any) {
+ s.log(args...)
+ s.T().SkipNow()
+}
+
+func (s *HstSuite) ResetContainers() {
+ for _, container := range s.containers {
+ container.stop()
+ }
+}
+
+func (s *HstSuite) RemoveVolumes() {
+ for _, volumeName := range s.volumes {
+ cmd := "docker volume rm " + volumeName
+ exechelper.Run(cmd)
+ os.RemoveAll(volumeName)
+ }
+}
+
+func (s *HstSuite) getContainerByName(name string) *Container {
+ return s.containers[name]
+}
+
+func (s *HstSuite) getContainerCopyByName(name string) *Container {
+ // Create a copy and return its address, so that individial tests which call this
+ // are not able to modify the original container and affect other tests by doing that
+ containerCopy := *s.containers[name]
+ return &containerCopy
+}
+
+func (s *HstSuite) loadContainerTopology(topologyName string) {
+ data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ for _, elem := range yamlTopo.Volumes {
+ volumeMap := elem["volume"].(VolumeConfig)
+ hostDir := volumeMap["host-dir"].(string)
+ s.volumes = append(s.volumes, hostDir)
+ }
+
+ s.containers = make(map[string]*Container)
+ for _, elem := range yamlTopo.Containers {
+ newContainer, err := NewContainer(elem)
+ newContainer.suite = s
+ if err != nil {
+ s.T().Fatalf("container config error: %v", err)
+ }
+ s.log(newContainer.getRunCommand())
+ s.containers[newContainer.name] = newContainer
+ }
+}
+
+func (s *HstSuite) loadNetworkTopology(topologyName string) {
+ data, err := ioutil.ReadFile(NetworkTopologyDir + topologyName + ".yaml")
+ if err != nil {
+ s.T().Fatalf("read error: %v", err)
+ }
+ var yamlTopo YamlTopology
+ err = yaml.Unmarshal(data, &yamlTopo)
+ if err != nil {
+ s.T().Fatalf("unmarshal error: %v", err)
+ }
+
+ s.networkNamespaces = make(map[string]*NetworkNamespace)
+ s.veths = make(map[string]*NetworkInterfaceVeth)
+ s.taps = make(map[string]*NetworkInterfaceTap)
+ s.bridges = make(map[string]*NetworkBridge)
+ for _, elem := range yamlTopo.Devices {
+ switch elem["type"].(string) {
+ case NetNs:
+ {
+ if namespace, err := NewNetNamespace(elem); err == nil {
+ s.networkNamespaces[namespace.Name()] = &namespace
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Veth:
+ {
+ if veth, err := NewVeth(elem); err == nil {
+ s.veths[veth.Name()] = &veth
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Tap:
+ {
+ if tap, err := NewTap(elem); err == nil {
+ s.taps[tap.Name()] = &tap
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ case Bridge:
+ {
+ if bridge, err := NewBridge(elem); err == nil {
+ s.bridges[bridge.Name()] = &bridge
+ } else {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ }
+ }
+}
+
+func (s *HstSuite) configureNetworkTopology(topologyName string) {
+ s.loadNetworkTopology(topologyName)
+
+ for _, ns := range s.networkNamespaces {
+ if err := ns.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, veth := range s.veths {
+ if err := veth.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, tap := range s.taps {
+ if err := tap.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+ for _, bridge := range s.bridges {
+ if err := bridge.Configure(); err != nil {
+ s.T().Fatalf("network config error: %v", err)
+ }
+ }
+}
+
+func (s *HstSuite) unconfigureNetworkTopology() {
+ if IsPersistent() {
+ return
+ }
+ for _, ns := range s.networkNamespaces {
+ ns.Unconfigure()
+ }
+ for _, veth := range s.veths {
+ veth.Unconfigure()
+ }
+ for _, tap := range s.taps {
+ tap.Unconfigure()
+ }
+ for _, bridge := range s.bridges {
+ bridge.Unconfigure()
+ }
+}
+
+func (s *HstSuite) NewAddress() (AddressWithPrefix, error) {
+ var ipPrefix AddressWithPrefix
+ var err error
+
+ if s.numberOfAddresses == 255 {
+ s.T().Fatalf("no available IPv4 addresses")
+ }
+
+ address := fmt.Sprintf("10.10.10.%v/24", s.numberOfAddresses+1)
+ ipPrefix, err = ip_types.ParseAddressWithPrefix(address)
+ if err != nil {
+ return AddressWithPrefix{}, err
+ }
+ s.numberOfAddresses++
+
+ return ipPrefix, nil
+}