blob: ff7024582dcc1bba194dcff74b1dea7841145a8c [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
Filip Tehlar671cf512023-01-31 10:34:18 +010020var IsPersistent = flag.Bool("persist", false, "persists topology config")
21var IsVerbose = flag.Bool("verbose", false, "verbose test output")
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010022
23type HstSuite struct {
24 suite.Suite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010025 containers map[string]*Container
26 volumes []string
27 netConfigs []NetConfig
Maros Ondrejicka40cba402023-02-23 13:19:15 +010028 netInterfaces map[string]*NetInterface
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010029 addresser *Addresser
Maros Ondrejickaa2d52622023-02-24 11:26:39 +010030 testIds map[string]string
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010031}
32
33func (s *HstSuite) TearDownSuite() {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010034 s.unconfigureNetworkTopology()
35}
36
37func (s *HstSuite) TearDownTest() {
Filip Tehlar671cf512023-01-31 10:34:18 +010038 if *IsPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010039 return
40 }
41 s.ResetContainers()
42 s.RemoveVolumes()
43}
44
45func (s *HstSuite) SetupTest() {
46 s.SetupVolumes()
47 s.SetupContainers()
48}
49
50func (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
58func (s *HstSuite) SetupContainers() {
59 for _, container := range s.containers {
60 if container.isOptional == false {
61 container.run()
62 }
63 }
64}
65
66func (s *HstSuite) hstFail() {
67 s.T().FailNow()
68}
69
70func (s *HstSuite) assertNil(object interface{}, msgAndArgs ...interface{}) {
71 if !assert.Nil(s.T(), object, msgAndArgs...) {
72 s.hstFail()
73 }
74}
75
76func (s *HstSuite) assertNotNil(object interface{}, msgAndArgs ...interface{}) {
77 if !assert.NotNil(s.T(), object, msgAndArgs...) {
78 s.hstFail()
79 }
80}
81
82func (s *HstSuite) assertEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
83 if !assert.Equal(s.T(), expected, actual, msgAndArgs...) {
84 s.hstFail()
85 }
86}
87
88func (s *HstSuite) assertNotEqual(expected, actual interface{}, msgAndArgs ...interface{}) {
89 if !assert.NotEqual(s.T(), expected, actual, msgAndArgs...) {
90 s.hstFail()
91 }
92}
93
94func (s *HstSuite) assertContains(testString, contains interface{}, msgAndArgs ...interface{}) {
95 if !assert.Contains(s.T(), testString, contains, msgAndArgs...) {
96 s.hstFail()
97 }
98}
99
100func (s *HstSuite) assertNotContains(testString, contains interface{}, msgAndArgs ...interface{}) {
101 if !assert.NotContains(s.T(), testString, contains, msgAndArgs...) {
102 s.hstFail()
103 }
104}
105
Maros Ondrejicka2ddb2fd2023-02-15 17:44:46 +0100106func (s *HstSuite) assertNotEmpty(object interface{}, msgAndArgs ...interface{}) {
107 if !assert.NotEmpty(s.T(), object, msgAndArgs...) {
108 s.hstFail()
109 }
110}
111
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100112func (s *HstSuite) log(args ...any) {
Filip Tehlar671cf512023-01-31 10:34:18 +0100113 if *IsVerbose {
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100114 s.T().Helper()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100115 s.T().Log(args...)
116 }
117}
118
119func (s *HstSuite) skip(args ...any) {
120 s.log(args...)
121 s.T().SkipNow()
122}
123
124func (s *HstSuite) ResetContainers() {
125 for _, container := range s.containers {
126 container.stop()
127 }
128}
129
130func (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
138func (s *HstSuite) getContainerByName(name string) *Container {
139 return s.containers[name]
140}
141
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100142/*
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 */
146func (s *HstSuite) getTransientContainerByName(name string) *Container {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100147 containerCopy := *s.containers[name]
148 return &containerCopy
149}
150
151func (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 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100175 s.containers[newContainer.name] = newContainer
176 }
177}
178
179func (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
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100190 s.addresser = NewAddresser(s)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100191 s.netInterfaces = make(map[string]*NetInterface)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100192 for _, elem := range yamlTopo.Devices {
193 switch elem["type"].(string) {
194 case NetNs:
195 {
196 if namespace, err := NewNetNamespace(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100197 s.netConfigs = append(s.netConfigs, &namespace)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100198 } else {
199 s.T().Fatalf("network config error: %v", err)
200 }
201 }
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100202 case Veth, Tap:
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100203 {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100204 if netIf, err := NewNetworkInterface(elem, s.addresser); err == nil {
205 s.netConfigs = append(s.netConfigs, netIf)
206 s.netInterfaces[netIf.Name()] = netIf
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100207 } else {
208 s.T().Fatalf("network config error: %v", err)
209 }
210 }
211 case Bridge:
212 {
213 if bridge, err := NewBridge(elem); err == nil {
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100214 s.netConfigs = append(s.netConfigs, &bridge)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100215 } else {
216 s.T().Fatalf("network config error: %v", err)
217 }
218 }
219 }
220 }
221}
222
223func (s *HstSuite) configureNetworkTopology(topologyName string) {
224 s.loadNetworkTopology(topologyName)
225
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100226 for _, nc := range s.netConfigs {
227 if err := nc.Configure(); err != nil {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100228 s.T().Fatalf("network config error: %v", err)
229 }
230 }
231}
232
233func (s *HstSuite) unconfigureNetworkTopology() {
Filip Tehlar671cf512023-01-31 10:34:18 +0100234 if *IsPersistent {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100235 return
236 }
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100237 for _, nc := range s.netConfigs {
238 nc.Unconfigure()
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100239 }
240}
241
Maros Ondrejickaa2d52622023-02-24 11:26:39 +0100242func (s *HstSuite) getTestId() string {
243 testName := s.T().Name()
244
245 if s.testIds == nil {
246 s.testIds = map[string]string{}
247 }
248
249 if _, ok := s.testIds[testName]; !ok {
250 s.testIds[testName] = time.Now().Format(time.RFC3339)
251 }
252
253 return s.testIds[testName]
254}
255
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100256type AddressCounter = int
257
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100258type Addresser struct {
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100259 networks map[int]AddressCounter
260 suite *HstSuite
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100261}
262
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100263func (a *Addresser) AddNetwork(networkNumber int) {
264 a.networks[networkNumber] = 1
265}
266
267func (a *Addresser) NewIp4Address(inputNetworkNumber ...int) (string, error) {
268 var networkNumber int = 0
269 if len(inputNetworkNumber) > 0 {
270 networkNumber = inputNetworkNumber[0]
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100271 }
272
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100273 if _, ok := a.networks[networkNumber]; !ok {
274 a.AddNetwork(networkNumber)
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100275 }
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100276
277 numberOfAddresses := a.networks[networkNumber]
278
279 if numberOfAddresses == 254 {
280 return "", fmt.Errorf("no available IPv4 addresses")
281 }
282
283 address := fmt.Sprintf("10.10.%v.%v/24", networkNumber, numberOfAddresses)
284 a.networks[networkNumber] = numberOfAddresses + 1
285
286 return address, nil
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100287}
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100288
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100289func NewAddresser(suite *HstSuite) *Addresser {
290 var addresser = new(Addresser)
291 addresser.suite = suite
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100292 addresser.networks = make(map[int]AddressCounter)
293 addresser.AddNetwork(0)
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100294 return addresser
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100295}