hs-test: refactor test cases from ns suite
[vpp.git] / extras / hs-test / hst_suite.go
1 package main
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "os"
7
8         "github.com/edwarnicke/exechelper"
9         "github.com/stretchr/testify/assert"
10         "github.com/stretchr/testify/suite"
11         "gopkg.in/yaml.v3"
12 )
13
14 const (
15         defaultNamespaceName string = "default"
16 )
17
18 func IsPersistent() bool {
19         return os.Getenv("HST_PERSIST") == "1"
20 }
21
22 func IsVerbose() bool {
23         return os.Getenv("HST_VERBOSE") == "1"
24 }
25
26 type HstSuite struct {
27         suite.Suite
28         teardownSuite func()
29         containers    map[string]*Container
30         volumes       []string
31         netConfigs    []NetConfig
32         netInterfaces map[string]NetInterface
33         addresser     *Addresser
34 }
35
36 func (s *HstSuite) TearDownSuite() {
37         if s.teardownSuite != nil {
38                 s.teardownSuite() // TODO remove this after config moved to SetupTest() for each suite
39         }
40
41         s.unconfigureNetworkTopology()
42 }
43
44 func (s *HstSuite) TearDownTest() {
45         if IsPersistent() {
46                 return
47         }
48         s.ResetContainers()
49         s.RemoveVolumes()
50 }
51
52 func (s *HstSuite) SetupTest() {
53         s.SetupVolumes()
54         s.SetupContainers()
55 }
56
57 func (s *HstSuite) SetupVolumes() {
58         for _, volume := range s.volumes {
59                 cmd := "docker volume create --name=" + volume
60                 s.log(cmd)
61                 exechelper.Run(cmd)
62         }
63 }
64
65 func (s *HstSuite) SetupContainers() {
66         for _, container := range s.containers {
67                 if container.isOptional == false {
68                         container.run()
69                 }
70         }
71 }
72
73 func (s *HstSuite) hstFail() {
74         s.T().FailNow()
75 }
76
77 func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
78         if !assert.Nil(s.T(), object, msgAndArgs...) {
79                 s.hstFail()
80         }
81 }
82
83 func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
84         if !assert.NotNil(s.T(), object, msgAndArgs...) {
85                 s.hstFail()
86         }
87 }
88
89 func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
90         if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
91                 s.hstFail()
92         }
93 }
94
95 func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
96         if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
97                 s.hstFail()
98         }
99 }
100
101 func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
102         if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
103                 s.hstFail()
104         }
105 }
106
107 func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
108         if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
109                 s.hstFail()
110         }
111 }
112
113 func (s *HstSuite) log(args ...any) {
114         if IsVerbose() {
115                 s.T().Log(args...)
116         }
117 }
118
119 func (s *HstSuite) skip(args ...any) {
120         s.log(args...)
121         s.T().SkipNow()
122 }
123
124 func (s *HstSuite) ResetContainers() {
125         for _, container := range s.containers {
126                 container.stop()
127         }
128 }
129
130 func (s *HstSuite) RemoveVolumes() {
131         for _, volumeName := range s.volumes {
132                 cmd := "docker volume rm " + volumeName
133                 exechelper.Run(cmd)
134                 os.RemoveAll(volumeName)
135         }
136 }
137
138 func (s *HstSuite) getContainerByName(name string) *Container {
139         return s.containers[name]
140 }
141
142 /*
143  * Create a copy and return its address, so that individial tests which call this
144  * are not able to modify the original container and affect other tests by doing that
145  */
146 func (s *HstSuite) getTransientContainerByName(name string) *Container {
147         containerCopy := *s.containers[name]
148         return &containerCopy
149 }
150
151 func (s *HstSuite) loadContainerTopology(topologyName string) {
152         data, err := ioutil.ReadFile(ContainerTopologyDir + topologyName + ".yaml")
153         if err != nil {
154                 s.T().Fatalf("read error: %v", err)
155         }
156         var yamlTopo YamlTopology
157         err = yaml.Unmarshal(data, &yamlTopo)
158         if err != nil {
159                 s.T().Fatalf("unmarshal error: %v", err)
160         }
161
162         for _, elem := range yamlTopo.Volumes {
163                 volumeMap := elem["volume"].(VolumeConfig)
164                 hostDir := volumeMap["host-dir"].(string)
165                 s.volumes = append(s.volumes, hostDir)
166         }
167
168         s.containers = make(map[string]*Container)
169         for _, elem := range yamlTopo.Containers {
170                 newContainer, err := NewContainer(elem)
171                 newContainer.suite = s
172                 if err != nil {
173                         s.T().Fatalf("container config error: %v", err)
174                 }
175                 s.log(newContainer.getRunCommand())
176                 s.containers[newContainer.name] = newContainer
177         }
178 }
179
180 func (s *HstSuite) loadNetworkTopology(topologyName string) {
181         data, err := ioutil.ReadFile(NetworkTopologyDir + topologyName + ".yaml")
182         if err != nil {
183                 s.T().Fatalf("read error: %v", err)
184         }
185         var yamlTopo YamlTopology
186         err = yaml.Unmarshal(data, &yamlTopo)
187         if err != nil {
188                 s.T().Fatalf("unmarshal error: %v", err)
189         }
190
191         s.addresser = NewAddresser(s)
192         s.netInterfaces = make(map[string]NetInterface)
193         for _, elem := range yamlTopo.Devices {
194                 switch elem["type"].(string) {
195                 case NetNs:
196                         {
197                                 if namespace, err := NewNetNamespace(elem); err == nil {
198                                         s.netConfigs = append(s.netConfigs, &namespace)
199                                 } else {
200                                         s.T().Fatalf("network config error: %v", err)
201                                 }
202                         }
203                 case Veth:
204                         {
205                                 if veth, err := NewVeth(elem, s.addresser); err == nil {
206                                         s.netConfigs = append(s.netConfigs, &veth)
207                                         s.netInterfaces[veth.Name()] = &veth
208                                 } else {
209                                         s.T().Fatalf("network config error: %v", err)
210                                 }
211                         }
212                 case Tap:
213                         {
214                                 if tap, err := NewTap(elem, s.addresser); err == nil {
215                                         s.netConfigs = append(s.netConfigs, &tap)
216                                         s.netInterfaces[tap.Name()] = &tap
217                                 } else {
218                                         s.T().Fatalf("network config error: %v", err)
219                                 }
220                         }
221                 case Bridge:
222                         {
223                                 if bridge, err := NewBridge(elem); err == nil {
224                                         s.netConfigs = append(s.netConfigs, &bridge)
225                                 } else {
226                                         s.T().Fatalf("network config error: %v", err)
227                                 }
228                         }
229                 }
230         }
231 }
232
233 func (s *HstSuite) configureNetworkTopology(topologyName string) {
234         s.loadNetworkTopology(topologyName)
235
236         for _, nc := range s.netConfigs {
237                 if err := nc.Configure(); err != nil {
238                         s.T().Fatalf("network config error: %v", err)
239                 }
240         }
241 }
242
243 func (s *HstSuite) unconfigureNetworkTopology() {
244         if IsPersistent() {
245                 return
246         }
247         for _, nc := range s.netConfigs {
248                 nc.Unconfigure()
249         }
250 }
251
252 type NamespaceAddresses struct {
253         namespace         string
254         numberOfAddresses int
255 }
256
257 type Addresser struct {
258         namespaces []*NamespaceAddresses
259         suite      *HstSuite
260 }
261
262 func (a *Addresser) AddNamespace(name string) {
263         var newNamespace = &NamespaceAddresses{
264                 namespace:         name,
265                 numberOfAddresses: 0,
266         }
267         a.namespaces = append(a.namespaces, newNamespace)
268 }
269
270 func (a *Addresser) NewIp4Address() (string, error) {
271         return a.NewIp4AddressWithNamespace(defaultNamespaceName)
272 }
273
274 func (a *Addresser) NewIp4AddressWithNamespace(namespace string) (string, error) {
275         for i, val := range a.namespaces {
276                 if val.namespace != namespace {
277                         continue
278                 }
279                 if val.numberOfAddresses == 255 {
280                         return "", fmt.Errorf("no available IPv4 addresses")
281                 }
282                 address := fmt.Sprintf("10.10.%v.%v/24", i, val.numberOfAddresses+1)
283                 val.numberOfAddresses++
284                 return address, nil
285         }
286         a.AddNamespace(namespace)
287         return a.NewIp4AddressWithNamespace(namespace)
288 }
289
290 func NewAddresser(suite *HstSuite) *Addresser {
291         var addresser = new(Addresser)
292         addresser.suite = suite
293         addresser.AddNamespace(defaultNamespaceName)
294         return addresser
295 }