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