blob: 093bca0b56f339857141fb0d9e6cbb75ac030ed1 [file] [log] [blame]
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01001package main
2
3import (
Filip Tehlar671cf512023-01-31 10:34:18 +01004 "flag"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01005 "io/ioutil"
6 "os"
Filip Tehlar31eaea92023-06-15 10:06:57 +02007 "os/exec"
8 "strings"
Maros Ondrejickaa2d52622023-02-24 11:26:39 +01009 "time"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010010
11 "github.com/edwarnicke/exechelper"
12 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/suite"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010014 "gopkg.in/yaml.v3"
15)
16
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010017const (
Filip Tehlar608d0062023-04-28 10:29:47 +020018 DEFAULT_NETWORK_NUM int = 1
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010019)
20
Maros Ondrejickae7625d02023-02-28 16:55:01 +010021var isPersistent = flag.Bool("persist", false, "persists topology config")
22var isVerbose = flag.Bool("verbose", false, "verbose test output")
23var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
24var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
Filip Tehlar608d0062023-04-28 10:29:47 +020025var nConfiguredCpus = flag.Int("cpus", 1, "number of CPUs assigned to vpp")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010026
27type HstSuite struct {
28 suite.Suite
Filip Tehlar3a910ab2023-06-08 17:39:39 +020029 containers map[string]*Container
30 volumes []string
31 netConfigs []NetConfig
32 netInterfaces map[string]*NetInterface
33 ip4AddrAllocator *Ip4AddressAllocator
34 testIds map[string]string
35 cpuAllocator *CpuAllocatorT
36 cpuContexts []*CpuContext
37 cpuPerVpp int
Filip Tehlar608d0062023-04-28 10:29:47 +020038}
39
40func (s *HstSuite) SetupSuite() {
41 var err error
42 s.cpuAllocator, err = CpuAllocator()
43 if err != nil {
44 s.FailNow("failed to init cpu allocator: %v", err)
45 }
46 s.cpuPerVpp = *nConfiguredCpus
47}
48
49func (s *HstSuite) AllocateCpus() []int {
50 cpuCtx, err := s.cpuAllocator.Allocate(s.cpuPerVpp)
51 s.assertNil(err)
52 s.AddCpuContext(cpuCtx)
53 return cpuCtx.cpus
54}
55
56func (s *HstSuite) AddCpuContext(cpuCtx *CpuContext) {
57 s.cpuContexts = append(s.cpuContexts, cpuCtx)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010058}
59
60func (s *HstSuite) TearDownSuite() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010061 s.unconfigureNetworkTopology()
62}
63
64func (s *HstSuite) TearDownTest() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010065 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010066 return
67 }
Filip Tehlar608d0062023-04-28 10:29:47 +020068 for _, c := range s.cpuContexts {
69 c.Release()
70 }
Maros Ondrejickae7625d02023-02-28 16:55:01 +010071 s.resetContainers()
72 s.removeVolumes()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010073}
74
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010075func (s *HstSuite) skipIfUnconfiguring() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010076 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010077 s.skip("skipping to unconfigure")
78 }
79}
80
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010081func (s *HstSuite) SetupTest() {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010082 s.skipIfUnconfiguring()
Maros Ondrejickae7625d02023-02-28 16:55:01 +010083 s.setupVolumes()
84 s.setupContainers()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010085}
86
Maros Ondrejickae7625d02023-02-28 16:55:01 +010087func (s *HstSuite) setupVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010088 for _, volume := range s.volumes {
89 cmd := "docker volume create --name=" + volume
90 s.log(cmd)
91 exechelper.Run(cmd)
92 }
93}
94
Maros Ondrejickae7625d02023-02-28 16:55:01 +010095func (s *HstSuite) setupContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010096 for _, container := range s.containers {
Filip Tehlar608d0062023-04-28 10:29:47 +020097 if !container.isOptional {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010098 container.run()
99 }
100 }
101}
102
103func (s *HstSuite) hstFail() {
104 s.T().FailNow()
105}
106
107func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
108 if !assert.Nil(s.T(), object, msgAndArgs...) {
109 s.hstFail()
110 }
111}
112
113func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
114 if !assert.NotNil(s.T(), object, msgAndArgs...) {
115 s.hstFail()
116 }
117}
118
119func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
120 if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
121 s.hstFail()
122 }
123}
124
125func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
126 if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
127 s.hstFail()
128 }
129}
130
131func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
132 if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
133 s.hstFail()
134 }
135}
136
137func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
138 if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
139 s.hstFail()
140 }
141}
142
Maros Ondrejicka2ddb2fd2023-02-15 17:44:46 +0100143func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
144 if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
145 s.hstFail()
146 }
147}
148
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100149func (s *HstSuite) log(args ...any) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100150 if *isVerbose {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100151 s.T().Helper()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100152 s.T().Log(args...)
153 }
154}
155
156func (s *HstSuite) skip(args ...any) {
157 s.log(args...)
158 s.T().SkipNow()
159}
160
Filip Tehlar608d0062023-04-28 10:29:47 +0200161func (s *HstSuite) SkipIfMultiWorker(args ...any) {
162 if *nConfiguredCpus > 1 {
163 s.skip("test case not supported with multiple vpp workers")
164 }
165}
166
Filip Tehlar31eaea92023-06-15 10:06:57 +0200167func (s *HstSuite) SkipUnlessExtendedTestsBuilt() {
168 imageName := "hs-test/nginx-http3"
169
170 cmd := exec.Command("docker", "images", imageName)
171 byteOutput, err := cmd.CombinedOutput()
172 if err != nil {
173 s.log("error while searching for docker image")
174 return
175 }
176 if !strings.Contains(string(byteOutput), imageName) {
177 s.skip("extended tests not built")
178 }
179}
180
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100181func (s *HstSuite) resetContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100182 for _, container := range s.containers {
183 container.stop()
184 }
185}
186
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100187func (s *HstSuite) removeVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100188 for _, volumeName := range s.volumes {
189 cmd := "docker volume rm " + volumeName
190 exechelper.Run(cmd)
191 os.RemoveAll(volumeName)
192 }
193}
194
195func (s *HstSuite) getContainerByName(name string) *Container {
196 return s.containers[name]
197}
198
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100199/*
200 * Create a copy and return its address, so that individial tests which call this
201 * are not able to modify the original container and affect other tests by doing that
202 */
203func (s *HstSuite) getTransientContainerByName(name string) *Container {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100204 containerCopy := *s.containers[name]
205 return &containerCopy
206}
207
208func (s *HstSuite) loadContainerTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100209 data, err := ioutil.ReadFile(containerTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100210 if err != nil {
211 s.T().Fatalf("read error: %v", err)
212 }
213 var yamlTopo YamlTopology
214 err = yaml.Unmarshal(data, &yamlTopo)
215 if err != nil {
216 s.T().Fatalf("unmarshal error: %v", err)
217 }
218
219 for _, elem := range yamlTopo.Volumes {
220 volumeMap := elem["volume"].(VolumeConfig)
221 hostDir := volumeMap["host-dir"].(string)
222 s.volumes = append(s.volumes, hostDir)
223 }
224
225 s.containers = make(map[string]*Container)
226 for _, elem := range yamlTopo.Containers {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100227 newContainer, err := newContainer(elem)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100228 newContainer.suite = s
229 if err != nil {
230 s.T().Fatalf("container config error: %v", err)
231 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100232 s.containers[newContainer.name] = newContainer
233 }
234}
235
236func (s *HstSuite) loadNetworkTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100237 data, err := ioutil.ReadFile(networkTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100238 if err != nil {
239 s.T().Fatalf("read error: %v", err)
240 }
241 var yamlTopo YamlTopology
242 err = yaml.Unmarshal(data, &yamlTopo)
243 if err != nil {
244 s.T().Fatalf("unmarshal error: %v", err)
245 }
246
Filip Tehlar3a910ab2023-06-08 17:39:39 +0200247 s.ip4AddrAllocator = NewIp4AddressAllocator()
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100248 s.netInterfaces = make(map[string]*NetInterface)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100249 for _, elem := range yamlTopo.Devices {
250 switch elem["type"].(string) {
251 case NetNs:
252 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100253 if namespace, err := newNetNamespace(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100254 s.netConfigs = append(s.netConfigs, &namespace)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100255 } else {
256 s.T().Fatalf("network config error: %v", err)
257 }
258 }
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100259 case Veth, Tap:
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100260 {
Filip Tehlar3a910ab2023-06-08 17:39:39 +0200261 if netIf, err := newNetworkInterface(elem, s.ip4AddrAllocator); err == nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100262 s.netConfigs = append(s.netConfigs, netIf)
263 s.netInterfaces[netIf.Name()] = netIf
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100264 } else {
265 s.T().Fatalf("network config error: %v", err)
266 }
267 }
268 case Bridge:
269 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100270 if bridge, err := newBridge(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100271 s.netConfigs = append(s.netConfigs, &bridge)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100272 } else {
273 s.T().Fatalf("network config error: %v", err)
274 }
275 }
276 }
277 }
278}
279
280func (s *HstSuite) configureNetworkTopology(topologyName string) {
281 s.loadNetworkTopology(topologyName)
282
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100283 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100284 return
285 }
286
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100287 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100288 if err := nc.configure(); err != nil {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100289 s.T().Fatalf("network config error: %v", err)
290 }
291 }
292}
293
294func (s *HstSuite) unconfigureNetworkTopology() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100295 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100296 return
297 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100298 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100299 nc.unconfigure()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100300 }
301}
302
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100303func (s *HstSuite) getTestId() string {
304 testName := s.T().Name()
305
306 if s.testIds == nil {
307 s.testIds = map[string]string{}
308 }
309
310 if _, ok := s.testIds[testName]; !ok {
Filip Tehlar75776f02023-03-24 13:47:45 +0100311 s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100312 }
313
314 return s.testIds[testName]
315}