blob: 042b4fe6b2493299c19d12e10d701f9da81ecd3e [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 "fmt"
6 "io/ioutil"
7 "os"
Maros Ondrejickaa2d52622023-02-24 11:26:39 +01008 "time"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01009
10 "github.com/edwarnicke/exechelper"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/suite"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010013 "gopkg.in/yaml.v3"
14)
15
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010016const (
Maros Ondrejicka300f70d2023-02-21 10:53:20 +010017 defaultNetworkNumber int = 1
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010018)
19
Maros Ondrejickae7625d02023-02-28 16:55:01 +010020var isPersistent = flag.Bool("persist", false, "persists topology config")
21var isVerbose = flag.Bool("verbose", false, "verbose test output")
22var isUnconfiguring = flag.Bool("unconfigure", false, "remove topology")
23var isVppDebug = flag.Bool("debug", false, "attach gdb to vpp")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010024
25type HstSuite struct {
26 suite.Suite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010027 containers map[string]*Container
28 volumes []string
29 netConfigs []NetConfig
Maros Ondrejicka40cba402023-02-23 13:19:15 +010030 netInterfaces map[string]*NetInterface
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010031 addresser *Addresser
Maros Ondrejickaa2d52622023-02-24 11:26:39 +010032 testIds map[string]string
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010033}
34
35func (s *HstSuite) TearDownSuite() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010036 s.unconfigureNetworkTopology()
37}
38
39func (s *HstSuite) TearDownTest() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010040 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010041 return
42 }
Maros Ondrejickae7625d02023-02-28 16:55:01 +010043 s.resetContainers()
44 s.removeVolumes()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010045}
46
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010047func (s *HstSuite) skipIfUnconfiguring() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010048 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010049 s.skip("skipping to unconfigure")
50 }
51}
52
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010053func (s *HstSuite) SetupTest() {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +010054 s.skipIfUnconfiguring()
Maros Ondrejickae7625d02023-02-28 16:55:01 +010055 s.setupVolumes()
56 s.setupContainers()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010057}
58
Maros Ondrejickae7625d02023-02-28 16:55:01 +010059func (s *HstSuite) setupVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010060 for _, volume := range s.volumes {
61 cmd := "docker volume create --name=" + volume
62 s.log(cmd)
63 exechelper.Run(cmd)
64 }
65}
66
Maros Ondrejickae7625d02023-02-28 16:55:01 +010067func (s *HstSuite) setupContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010068 for _, container := range s.containers {
69 if container.isOptional == false {
70 container.run()
71 }
72 }
73}
74
75func (s *HstSuite) hstFail() {
76 s.T().FailNow()
77}
78
79func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
80 if !assert.Nil(s.T(), object, msgAndArgs...) {
81 s.hstFail()
82 }
83}
84
85func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
86 if !assert.NotNil(s.T(), object, msgAndArgs...) {
87 s.hstFail()
88 }
89}
90
91func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
92 if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
93 s.hstFail()
94 }
95}
96
97func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
98 if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
99 s.hstFail()
100 }
101}
102
103func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
104 if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
105 s.hstFail()
106 }
107}
108
109func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
110 if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
111 s.hstFail()
112 }
113}
114
Maros Ondrejicka2ddb2fd2023-02-15 17:44:46 +0100115func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
116 if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
117 s.hstFail()
118 }
119}
120
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100121func (s *HstSuite) log(args ...any) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100122 if *isVerbose {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100123 s.T().Helper()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100124 s.T().Log(args...)
125 }
126}
127
128func (s *HstSuite) skip(args ...any) {
129 s.log(args...)
130 s.T().SkipNow()
131}
132
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100133func (s *HstSuite) resetContainers() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100134 for _, container := range s.containers {
135 container.stop()
136 }
137}
138
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100139func (s *HstSuite) removeVolumes() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100140 for _, volumeName := range s.volumes {
141 cmd := "docker volume rm " + volumeName
142 exechelper.Run(cmd)
143 os.RemoveAll(volumeName)
144 }
145}
146
147func (s *HstSuite) getContainerByName(name string) *Container {
148 return s.containers[name]
149}
150
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100151/*
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 */
155func (s *HstSuite) getTransientContainerByName(name string) *Container {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100156 containerCopy := *s.containers[name]
157 return &containerCopy
158}
159
160func (s *HstSuite) loadContainerTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100161 data, err := ioutil.ReadFile(containerTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100162 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 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100179 newContainer, err := newContainer(elem)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100180 newContainer.suite = s
181 if err != nil {
182 s.T().Fatalf("container config error: %v", err)
183 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100184 s.containers[newContainer.name] = newContainer
185 }
186}
187
188func (s *HstSuite) loadNetworkTopology(topologyName string) {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100189 data, err := ioutil.ReadFile(networkTopologyDir + topologyName + ".yaml")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100190 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
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100199 s.addresser = newAddresser(s)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100200 s.netInterfaces = make(map[string]*NetInterface)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100201 for _, elem := range yamlTopo.Devices {
202 switch elem["type"].(string) {
203 case NetNs:
204 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100205 if namespace, err := newNetNamespace(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100206 s.netConfigs = append(s.netConfigs, &namespace)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100207 } else {
208 s.T().Fatalf("network config error: %v", err)
209 }
210 }
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100211 case Veth, Tap:
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100212 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100213 if netIf, err := newNetworkInterface(elem, s.addresser); err == nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100214 s.netConfigs = append(s.netConfigs, netIf)
215 s.netInterfaces[netIf.Name()] = netIf
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100216 } else {
217 s.T().Fatalf("network config error: %v", err)
218 }
219 }
220 case Bridge:
221 {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100222 if bridge, err := newBridge(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100223 s.netConfigs = append(s.netConfigs, &bridge)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100224 } else {
225 s.T().Fatalf("network config error: %v", err)
226 }
227 }
228 }
229 }
230}
231
232func (s *HstSuite) configureNetworkTopology(topologyName string) {
233 s.loadNetworkTopology(topologyName)
234
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100235 if *isUnconfiguring {
Maros Ondrejickaaf004dd2023-02-27 16:52:57 +0100236 return
237 }
238
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100239 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100240 if err := nc.configure(); err != nil {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100241 s.T().Fatalf("network config error: %v", err)
242 }
243 }
244}
245
246func (s *HstSuite) unconfigureNetworkTopology() {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100247 if *isPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100248 return
249 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100250 for _, nc := range s.netConfigs {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100251 nc.unconfigure()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100252 }
253}
254
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100255func (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 {
Filip Tehlar75776f02023-03-24 13:47:45 +0100263 s.testIds[testName] = time.Now().Format("2006-01-02_15-04-05")
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100264 }
265
266 return s.testIds[testName]
267}
268
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100269type AddressCounter = int
270
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100271type Addresser struct {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100272 networks map[int]AddressCounter
273 suite *HstSuite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100274}
275
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100276func (a *Addresser) addNetwork(networkNumber int) {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100277 a.networks[networkNumber] = 1
278}
279
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100280func (a *Addresser) newIp4Address(inputNetworkNumber ...int) (string, error) {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100281 var networkNumber int = 0
282 if len(inputNetworkNumber) > 0 {
283 networkNumber = inputNetworkNumber[0]
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100284 }
285
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100286 if _, ok := a.networks[networkNumber]; !ok {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100287 a.addNetwork(networkNumber)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100288 }
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100289
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
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100300}
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100301
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100302func newAddresser(suite *HstSuite) *Addresser {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100303 var addresser = new(Addresser)
304 addresser.suite = suite
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100305 addresser.networks = make(map[int]AddressCounter)
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100306 addresser.addNetwork(0)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100307 return addresser
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100308}