hs-test: store logs
[vpp.git] / extras / hs-test / hst_suite.go
1 package main
2
3 import (
4         "flag"
5         "fmt"
6         "io/ioutil"
7         "os"
8         "time"
9
10         "github.com/edwarnicke/exechelper"
11         "github.com/stretchr/testify/assert"
12         "github.com/stretchr/testify/suite"
13         "gopkg.in/yaml.v3"
14 )
15
16 const (
17         defaultNetworkNumber int = 1
18 )
19
20 var IsPersistent = flag.Bool("persist", false, "persists topology config")
21 var IsVerbose = flag.Bool("verbose", false, "verbose test output")
22
23 type HstSuite struct {
24         suite.Suite
25         containers    map[string]*Container
26         volumes       []string
27         netConfigs    []NetConfig
28         netInterfaces map[string]NetInterface
29         addresser     *Addresser
30         testIds       map[string]string
31 }
32
33 func (s *HstSuite) TearDownSuite() {
34         s.unconfigureNetworkTopology()
35 }
36
37 func (s *HstSuite) TearDownTest() {
38         if *IsPersistent {
39                 return
40         }
41         s.ResetContainers()
42         s.RemoveVolumes()
43 }
44
45 func (s *HstSuite) SetupTest() {
46         s.SetupVolumes()
47         s.SetupContainers()
48 }
49
50 func (s *HstSuite) SetupVolumes() {
51         for _, volume := range s.volumes {
52                 cmd := "docker volume create --name=" + volume
53                 s.log(cmd)
54                 exechelper.Run(cmd)
55         }
56 }
57
58 func (s *HstSuite) SetupContainers() {
59         for _, container := range s.containers {
60                 if container.isOptional == false {
61                         container.run()
62                 }
63         }
64 }
65
66 func (s *HstSuite) hstFail() {
67         s.T().FailNow()
68 }
69
70 func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
71         if !assert.Nil(s.T(), object, msgAndArgs...) {
72                 s.hstFail()
73         }
74 }
75
76 func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
77         if !assert.NotNil(s.T(), object, msgAndArgs...) {
78                 s.hstFail()
79         }
80 }
81
82 func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
83         if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
84                 s.hstFail()
85         }
86 }
87
88 func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
89         if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
90                 s.hstFail()
91         }
92 }
93
94 func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
95         if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
96                 s.hstFail()
97         }
98 }
99
100 func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
101         if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
102                 s.hstFail()
103         }
104 }
105
106 func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
107         if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
108                 s.hstFail()
109         }
110 }
111
112 func (s *HstSuite) log(args ...any) {
113         if *IsVerbose {
114                 s.T().Helper()
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.containers[newContainer.name] = newContainer
176         }
177 }
178
179 func (s *HstSuite) loadNetworkTopology(topologyName string) {
180         data, err := ioutil.ReadFile(NetworkTopologyDir + topologyName + ".yaml")
181         if err != nil {
182                 s.T().Fatalf("read error: %v", err)
183         }
184         var yamlTopo YamlTopology
185         err = yaml.Unmarshal(data, &yamlTopo)
186         if err != nil {
187                 s.T().Fatalf("unmarshal error: %v", err)
188         }
189
190         s.addresser = NewAddresser(s)
191         s.netInterfaces = make(map[string]NetInterface)
192         for _, elem := range yamlTopo.Devices {
193                 switch elem["type"].(string) {
194                 case NetNs:
195                         {
196                                 if namespace, err := NewNetNamespace(elem); err == nil {
197                                         s.netConfigs = append(s.netConfigs, &namespace)
198                                 } else {
199                                         s.T().Fatalf("network config error: %v", err)
200                                 }
201                         }
202                 case Veth:
203                         {
204                                 if veth, err := NewVeth(elem, s.addresser); err == nil {
205                                         s.netConfigs = append(s.netConfigs, &veth)
206                                         s.netInterfaces[veth.Name()] = &veth
207                                 } else {
208                                         s.T().Fatalf("network config error: %v", err)
209                                 }
210                         }
211                 case Tap:
212                         {
213                                 if tap, err := NewTap(elem, s.addresser); err == nil {
214                                         s.netConfigs = append(s.netConfigs, &tap)
215                                         s.netInterfaces[tap.Name()] = &tap
216                                 } else {
217                                         s.T().Fatalf("network config error: %v", err)
218                                 }
219                         }
220                 case Bridge:
221                         {
222                                 if bridge, err := NewBridge(elem); err == nil {
223                                         s.netConfigs = append(s.netConfigs, &bridge)
224                                 } else {
225                                         s.T().Fatalf("network config error: %v", err)
226                                 }
227                         }
228                 }
229         }
230 }
231
232 func (s *HstSuite) configureNetworkTopology(topologyName string) {
233         s.loadNetworkTopology(topologyName)
234
235         for _, nc := range s.netConfigs {
236                 if err := nc.Configure(); err != nil {
237                         s.T().Fatalf("network config error: %v", err)
238                 }
239         }
240 }
241
242 func (s *HstSuite) unconfigureNetworkTopology() {
243         if *IsPersistent {
244                 return
245         }
246         for _, nc := range s.netConfigs {
247                 nc.Unconfigure()
248         }
249 }
250
251 func (s *HstSuite) getTestId() string {
252         testName := s.T().Name()
253
254         if s.testIds == nil {
255                 s.testIds = map[string]string{}
256         }
257
258         if _, ok := s.testIds[testName]; !ok {
259                 s.testIds[testName] = time.Now().Format(time.RFC3339)
260         }
261
262         return s.testIds[testName]
263 }
264
265 type NetworkAddresses struct {
266         network           int
267         numberOfAddresses int
268 }
269
270 type AddressCounter = int
271
272 type Addresser struct {
273         networks map[int]AddressCounter
274         suite    *HstSuite
275 }
276
277 func (a *Addresser) AddNetwork(networkNumber int) {
278         a.networks[networkNumber] = 1
279 }
280
281 func (a *Addresser) NewIp4Address(inputNetworkNumber ...int) (string, error) {
282         var networkNumber int = 0
283         if len(inputNetworkNumber) > 0 {
284                 networkNumber = inputNetworkNumber[0]
285         }
286
287         if _, ok := a.networks[networkNumber]; !ok {
288                 a.AddNetwork(networkNumber)
289         }
290
291         numberOfAddresses := a.networks[networkNumber]
292
293         if numberOfAddresses == 254 {
294                 return "", fmt.Errorf("no available IPv4 addresses")
295         }
296
297         address := fmt.Sprintf("10.10.%v.%v/24", networkNumber, numberOfAddresses)
298         a.networks[networkNumber] = numberOfAddresses + 1
299
300         return address, nil
301 }
302
303 func NewAddresser(suite *HstSuite) *Addresser {
304         var addresser = new(Addresser)
305         addresser.suite = suite
306         addresser.networks = make(map[int]AddressCounter)
307         addresser.AddNetwork(0)
308         return addresser
309 }