hs-test: remove colons from log files
[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 var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
23 var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
24
25 type HstSuite struct {
26         suite.Suite
27         containers    map[string]*Container
28         volumes       []string
29         netConfigs    []NetConfig
30         netInterfaces map[string]*NetInterface
31         addresser     *Addresser
32         testIds       map[string]string
33 }
34
35 func (s *HstSuite) TearDownSuite() {
36         s.unconfigureNetworkTopology()
37 }
38
39 func (s *HstSuite) TearDownTest() {
40         if *isPersistent {
41                 return
42         }
43         s.resetContainers()
44         s.removeVolumes()
45 }
46
47 func (s *HstSuite) skipIfUnconfiguring() {
48         if *isUnconfiguring {
49                 s.skip("skipping to unconfigure")
50         }
51 }
52
53 func (s *HstSuite) SetupTest() {
54         s.skipIfUnconfiguring()
55         s.setupVolumes()
56         s.setupContainers()
57 }
58
59 func (s *HstSuite) setupVolumes() {
60         for _, volume := range s.volumes {
61                 cmd := "docker volume create --name=" + volume
62                 s.log(cmd)
63                 exechelper.Run(cmd)
64         }
65 }
66
67 func (s *HstSuite) setupContainers() {
68         for _, container := range s.containers {
69                 if container.isOptional == false {
70                         container.run()
71                 }
72         }
73 }
74
75 func (s *HstSuite) hstFail() {
76         s.T().FailNow()
77 }
78
79 func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
80         if !assert.Nil(s.T(), object, msgAndArgs...) {
81                 s.hstFail()
82         }
83 }
84
85 func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
86         if !assert.NotNil(s.T(), object, msgAndArgs...) {
87                 s.hstFail()
88         }
89 }
90
91 func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
92         if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
93                 s.hstFail()
94         }
95 }
96
97 func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
98         if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
99                 s.hstFail()
100         }
101 }
102
103 func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
104         if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
105                 s.hstFail()
106         }
107 }
108
109 func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
110         if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
111                 s.hstFail()
112         }
113 }
114
115 func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
116         if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
117                 s.hstFail()
118         }
119 }
120
121 func (s *HstSuite) log(args ...any) {
122         if *isVerbose {
123                 s.T().Helper()
124                 s.T().Log(args...)
125         }
126 }
127
128 func (s *HstSuite) skip(args ...any) {
129         s.log(args...)
130         s.T().SkipNow()
131 }
132
133 func (s *HstSuite) resetContainers() {
134         for _, container := range s.containers {
135                 container.stop()
136         }
137 }
138
139 func (s *HstSuite) removeVolumes() {
140         for _, volumeName := range s.volumes {
141                 cmd := "docker volume rm " + volumeName
142                 exechelper.Run(cmd)
143                 os.RemoveAll(volumeName)
144         }
145 }
146
147 func (s *HstSuite) getContainerByName(name string) *Container {
148         return s.containers[name]
149 }
150
151 /*
152  * Create a copy and return its address, so that individial tests which call this
153  * are not able to modify the original container and affect other tests by doing that
154  */
155 func (s *HstSuite) getTransientContainerByName(name string) *Container {
156         containerCopy := *s.containers[name]
157         return &containerCopy
158 }
159
160 func (s *HstSuite) loadContainerTopology(topologyName string) {
161         data, err := ioutil.ReadFile(containerTopologyDir + topologyName + ".yaml")
162         if err != nil {
163                 s.T().Fatalf("read error: %v", err)
164         }
165         var yamlTopo YamlTopology
166         err = yaml.Unmarshal(data, &yamlTopo)
167         if err != nil {
168                 s.T().Fatalf("unmarshal error: %v", err)
169         }
170
171         for _, elem := range yamlTopo.Volumes {
172                 volumeMap := elem["volume"].(VolumeConfig)
173                 hostDir := volumeMap["host-dir"].(string)
174                 s.volumes = append(s.volumes, hostDir)
175         }
176
177         s.containers = make(map[string]*Container)
178         for _, elem := range yamlTopo.Containers {
179                 newContainer, err := newContainer(elem)
180                 newContainer.suite = s
181                 if err != nil {
182                         s.T().Fatalf("container config error: %v", err)
183                 }
184                 s.containers[newContainer.name] = newContainer
185         }
186 }
187
188 func (s *HstSuite) loadNetworkTopology(topologyName string) {
189         data, err := ioutil.ReadFile(networkTopologyDir + topologyName + ".yaml")
190         if err != nil {
191                 s.T().Fatalf("read error: %v", err)
192         }
193         var yamlTopo YamlTopology
194         err = yaml.Unmarshal(data, &yamlTopo)
195         if err != nil {
196                 s.T().Fatalf("unmarshal error: %v", err)
197         }
198
199         s.addresser = newAddresser(s)
200         s.netInterfaces = make(map[string]*NetInterface)
201         for _, elem := range yamlTopo.Devices {
202                 switch elem["type"].(string) {
203                 case NetNs:
204                         {
205                                 if namespace, err := newNetNamespace(elem); err == nil {
206                                         s.netConfigs = append(s.netConfigs, &namespace)
207                                 } else {
208                                         s.T().Fatalf("network config error: %v", err)
209                                 }
210                         }
211                 case Veth, Tap:
212                         {
213                                 if netIf, err := newNetworkInterface(elem, s.addresser); err == nil {
214                                         s.netConfigs = append(s.netConfigs, netIf)
215                                         s.netInterfaces[netIf.Name()] = netIf
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         if *isUnconfiguring {
236                 return
237         }
238
239         for _, nc := range s.netConfigs {
240                 if err := nc.configure(); err != nil {
241                         s.T().Fatalf("network config error: %v", err)
242                 }
243         }
244 }
245
246 func (s *HstSuite) unconfigureNetworkTopology() {
247         if *isPersistent {
248                 return
249         }
250         for _, nc := range s.netConfigs {
251                 nc.unconfigure()
252         }
253 }
254
255 func (s *HstSuite) getTestId() string {
256         testName := s.T().Name()
257
258         if s.testIds == nil {
259                 s.testIds = map[string]string{}
260         }
261
262         if _, ok := s.testIds[testName]; !ok {
263                 s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
264         }
265
266         return s.testIds[testName]
267 }
268
269 type AddressCounter = int
270
271 type Addresser struct {
272         networks map[int]AddressCounter
273         suite    *HstSuite
274 }
275
276 func (a *Addresser) addNetwork(networkNumber int) {
277         a.networks[networkNumber] = 1
278 }
279
280 func (a *Addresser) newIp4Address(inputNetworkNumber ...int) (string, error) {
281         var networkNumber int = 0
282         if len(inputNetworkNumber) > 0 {
283                 networkNumber = inputNetworkNumber[0]
284         }
285
286         if _, ok := a.networks[networkNumber]; !ok {
287                 a.addNetwork(networkNumber)
288         }
289
290         numberOfAddresses := a.networks[networkNumber]
291
292         if numberOfAddresses == 254 {
293                 return "", fmt.Errorf("no available IPv4 addresses")
294         }
295
296         address := fmt.Sprintf("10.10.%v.%v/24", networkNumber, numberOfAddresses)
297         a.networks[networkNumber] = numberOfAddresses + 1
298
299         return address, nil
300 }
301
302 func newAddresser(suite *HstSuite) *Addresser {
303         var addresser = new(Addresser)
304         addresser.suite = suite
305         addresser.networks = make(map[int]AddressCounter)
306         addresser.addNetwork(0)
307         return addresser
308 }