blob: 939cc2419399834dddd0eeaff1224833920aec9a [file] [log] [blame]
sslobodrd6e07e72019-01-31 16:07:20 -05001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16// gRPC affinity router with active/active backends
17
18package main
19
20// Command line parameters and parsing
21import (
22 "os"
23 "fmt"
24 "flag"
25 "path"
26 //"bufio"
27 "errors"
28 "os/exec"
29 "strconv"
30 "io/ioutil"
31 "encoding/json"
32 "text/template"
33 "github.com/golang/protobuf/proto"
34 "github.com/opencord/voltha-go/common/log"
35 pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
36)
37
38type TestConfig struct {
39 configFile *string
40 logLevel *int
41 grpcLog *bool
42 Suites []string `json:"suites"`
43}
44
45
46type Connection struct {
47 Name string `json:"name"`
48 Port string `json:"port"`
49}
50
51type ProtoFile struct {
52 ImportPath string `json:"importPath"`
53 Service string `json:"service"`
54 Package string `json:"package"`
55}
56
57type ProtoSubst struct {
58 From string `json:"from"`
59 To string `json:"to"`
60}
61
62type Environment struct {
63 Command string `json:"cmdLine"`
64 ProtoFiles []ProtoFile `json:"protoFiles"`
65 ProtoDesc string `json:"protoDesc"`
66 Clients []Connection `json:"clients"`
67 Servers []Connection `json:"servers"`
68 Imports []string `json:"imports"`
69 ProtoSubsts []ProtoSubst `json:"protoSubst"`
70}
71
72type Rpc struct {
73 Client string `json:"client"`
74 Method string `json:"method"`
75 Param string `json:"param"`
76 Expect string `json:"expect"`
sslobodrd9daabf2019-02-05 13:14:21 -050077 MetaData []MetaD `json:"meta"`
78 ExpectMeta []MetaD `json:"expectMeta"`
sslobodrd6e07e72019-01-31 16:07:20 -050079}
80
81type MetaD struct {
82 Key string `json:"key"`
83 Val string `json:"value"`
84}
85
86type Server struct {
87 Name string `json:"name"`
88 Meta []MetaD `json:"meta"`
89}
90
91type Test struct {
92 Name string `json:"name"`
93 Send Rpc `json:"send"`
94 Servers []Server `json:"servers"`
95}
96
97type TestSuite struct {
98 Env Environment `json:"environment"`
99 Tests []Test `json:"tests"`
100}
101
102type ProtoImport struct {
103 Service string
104 Short string
105 Package string
106}
107
108type SendItem struct {
109 Client string
110 Method string
111 Param string
112 ParamType string
113 Expect string
114 ExpectType string
sslobodrd9daabf2019-02-05 13:14:21 -0500115 MetaData []MetaD
116 ExpectMeta []MetaD
sslobodrd6e07e72019-01-31 16:07:20 -0500117}
118
119type TestCase struct {
120 Name string
121 Send SendItem
122 Srvr []Server
123}
124
125type TestList struct {
126 ProtoImports []ProtoImport
127 Imports []string
128 Tests []TestCase
129}
130
131type ClientConfig struct {
132 Ct int
133 Name string
134 Port string
135 Imports []string
136 Methods map[string]*mthd
137 ProtoImports []ProtoImport
138}
139
140type ServerConfig struct {
141 Ct int
142 Name string
143 Port string
144 Imports []string
145 Methods map[string]*mthd
146 ProtoImports []ProtoImport
147}
148
149type mthd struct {
150 Pkg string
151 Svc string
152 Name string
153 Param string
154 Rtrn string
155 Ss bool // Server streaming
156 Cs bool // Clieent streaming
157}
158
159
160func parseCmd() (*TestConfig, error) {
161 config := &TestConfig{}
162 cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError);
163 config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
164 config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
165 config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
166
167 err := cmdParse.Parse(os.Args[1:]);
168 if err != nil {
169 //return err
170 return nil, errors.New("Error parsing the command line");
171 }
172 return config, nil
173}
174
175func (conf * TestConfig) loadConfig() error {
176
177 configF, err := os.Open(*conf.configFile);
178 log.Info("Loading configuration from: ", *conf.configFile)
179 if err != nil {
180 log.Error(err)
181 return err
182 }
183
184 defer configF.Close()
185
186 if configBytes, err := ioutil.ReadAll(configF); err != nil {
187 log.Error(err)
188 return err
189 } else if err := json.Unmarshal(configBytes, conf); err != nil {
190 log.Error(err)
191 return err
192 }
193
194 return nil
195}
196
197func (suite * TestSuite) loadSuite(fileN string) error {
198 suiteF, err := os.Open(fileN);
199 log.Info("Loading test suite from: ", fileN)
200 if err != nil {
201 log.Error(err)
202 return err
203 }
204
205 defer suiteF.Close()
206
207 if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
208 log.Error(err)
209 return err
210 } else if err := json.Unmarshal(suiteBytes, suite); err != nil {
211 log.Error(err)
212 return err
213 }
214
215 return nil
216}
217
218func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
219 var mthds map[string]*mthd = make(map[string]*mthd)
220 var rtrn_err bool
221
222 // Load the protobuf descriptor file
223 protoDescriptor := &pb.FileDescriptorSet{}
224 fb, err := ioutil.ReadFile(fileName);
225 if err != nil {
226 log.Errorf("Could not open proto file '%s'",fileName)
227 rtrn_err = true
228 }
229 err = proto.Unmarshal(fb, protoDescriptor)
230 if err != nil {
231 log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
232 rtrn_err = true
233 }
234
235 var substM map[string]string = make(map[string]string)
236 // Create a substitution map
237 log.Debugf("Creating import map")
238 for _,v := range substs {
239 log.Debugf("Mapping from %s to %s", v.From, v.To)
240 substM[v.From] = v.To
241 }
242
243
244 // Build the a map containing the method as the key
245 // and the paramter and return types as the fields
246 for _,f := range protoDescriptor.File {
247 if *f.Package == pkg {
248 for _, s:= range f.Service {
249 if *s.Name == svc {
250 log.Debugf("Loading package data '%s' for service '%s'", *f.Package, *s.Name)
251 // Now create a map keyed by method name with the value being the
252 // field number of the route selector.
253 //var ok bool
254 for _,m := range s.Method {
255 // Find the input type in the messages and extract the
256 // field number and save it for future reference.
257 log.Debugf("Processing method (%s(%s) (%s){}",*m.Name, (*m.InputType)[1:], (*m.OutputType)[1:])
258 mthds[*m.Name] = &mthd{Pkg:pkg, Svc:svc, Name:*m.Name, Param:(*m.InputType)[1:],
259 Rtrn:(*m.OutputType)[1:]}
260 if m.ClientStreaming != nil && *m.ClientStreaming == true {
261 log.Debugf("Method %s is a client streaming method", *m.Name)
262 mthds[*m.Name].Cs = true
263 }
264 if m.ServerStreaming != nil && *m.ServerStreaming == true {
265 log.Debugf("Method %s is a server streaming method", *m.Name)
266 mthds[*m.Name].Ss = true
267 }
268 // Perform the required substitutions
269 if _,ok := substM[mthds[*m.Name].Param]; ok == true {
270 mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
271 }
272 if _,ok := substM[mthds[*m.Name].Rtrn]; ok == true {
273 mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
274 }
275 }
276 }
277 }
278 }
279 }
280 if rtrn_err {
281 return nil,errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'",fileName))
282 }
283 return mthds, nil
284}
285
286// Server source code generation
287func generateServers(conf *TestConfig, suiteDir string, ts * TestSuite,
288 t *template.Template) error {
289 var servers []ServerConfig
290
291 for k,v := range ts.Env.Servers {
292 log.Infof("Generating the code for server[%d]: %s", k, v.Name)
293 sc := &ServerConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Imports}
294 for k1,v1 := range ts.Env.ProtoFiles {
295 imp := &ProtoImport{Short:"pb"+strconv.Itoa(k1),
296 Package:v1.ImportPath+v1.Package,
297 Service:v1.Service}
298 imp = &ProtoImport{Short:v1.Package,
299 Package:v1.ImportPath+v1.Package,
300 Service:v1.Service}
301 sc.ProtoImports = append(sc.ProtoImports, *imp)
302 // Compile the template from the file
303 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
304 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
305 v1.Service, ts.Env.ProtoSubsts); err != nil {
306 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
307 ts.Env.ProtoDesc, v1.Package, v1.Service)
308 return err
309 } else {
310 //Generate all the function calls required by the
311 sc.Methods = mthds
312 }
313 }
314 log.Debugf("Server: %v", *sc)
315 // Save this server for the next steop
316 servers = append(servers, *sc)
317 // Open an output file to put the output in.
318 if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
319 defer f.Close()
320 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
321 if err := t.ExecuteTemplate(f, "server.go", *sc); err != nil {
322 log.Errorf("Unable to execute template for server[%d]: %s: %v", k, v.Name, err)
323 return err
324 }
325 }
326 }
327 // Generate the server initialization code
328 if f,err := os.Create(suiteDir+"/serverInit.go"); err == nil {
329 defer f.Close()
330 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
331 if err := t.ExecuteTemplate(f, "serverInit.go", servers); err != nil {
332 log.Errorf("Unable to execute template for serverInit.go: %v", err)
333 return err
334 }
335 }
336
337 return nil
338}
339
340func generateClients(conf *TestConfig, suiteDir string, ts * TestSuite,
341 t *template.Template) error {
342 var clients []ClientConfig
343 for k,v := range ts.Env.Clients {
344 log.Infof("Generating the code for client[%d]: %s", k, v.Name)
345 cc := &ClientConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Imports}
346 // TODO: This loop makes no sense, the only proto map that would remain
347 // after this loop is the last one loaded. Fix this to load the map
348 // for all services not just the last one.
349 for _,v1 := range ts.Env.ProtoFiles {
350 imp := &ProtoImport{Short:v1.Package,
351 Package:v1.ImportPath+v1.Package,
352 Service:v1.Service}
353 cc.ProtoImports = append(cc.ProtoImports, *imp)
354 // Compile the template from the file
355 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
356 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
357 v1.Service, ts.Env.ProtoSubsts); err != nil {
358 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
359 ts.Env.ProtoDesc, v1.Package, v1.Service)
360 return err
361 } else {
362 //Generate all the function calls required by the
363 cc.Methods = mthds
364 }
365 }
366 clients = append(clients, *cc)
367 if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
368 _=f
369 defer f.Close()
370 if err := t.ExecuteTemplate(f, "client.go", cc); err != nil {
371 log.Errorf("Unable to execute template for client.go: %v", err)
372 return err
373 }
374 } else {
375 log.Errorf("Couldn't create file %s : %v", suiteDir+"/client.go", err)
376 return err
377 }
378 }
379 if f,err := os.Create(suiteDir+"/clientInit.go"); err == nil {
380 defer f.Close()
381 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
382 if err := t.ExecuteTemplate(f, "clientInit.go", clients); err != nil {
383 log.Errorf("Unable to execute template for clientInit.go: %v", err)
384 return err
385 }
386 }
387 return nil
388}
389
390func serverExists(srvr string, ts * TestSuite) bool {
391 for _,v := range ts.Env.Servers {
392 if v.Name == srvr {
393 return true
394 }
395 }
396 return false
397}
398func generateTestCases(conf *TestConfig, suiteDir string, ts * TestSuite,
399 t *template.Template) error {
400 var mthdMap map[string]*mthd
401 // Generate the test cases
402 log.Info("Generating the test cases: runTests.go")
403 tc := &TestList{Imports:ts.Env.Imports}
404
405 // Load the proto descriptor file
406 // TODO: This loop makes no sense, the only proto map that would remain
407 // after this loop is the last one loaded. Fix this to load the map
408 // for all services not just the last one.
409 for _,v := range ts.Env.ProtoFiles {
410 imp := &ProtoImport{Short:v.Package,
411 Package:v.ImportPath+v.Package,
412 Service:v.Service}
413 tc.ProtoImports = append(tc.ProtoImports, *imp)
414 // Compile the template from the file
415 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
416 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
417 v.Service, ts.Env.ProtoSubsts); err != nil {
418 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
419 ts.Env.ProtoDesc, v.Package, v.Service)
420 return err
421 } else {
422 mthdMap = mthds
423 }
424 }
425 // Create the test data structure for the template
426 for _,v := range ts.Tests {
427 var test TestCase
428
429 test.Name = v.Name
430 test.Send.Client = v.Send.Client
431 test.Send.Method = v.Send.Method
432 test.Send.Param = v.Send.Param
433 test.Send.ParamType = mthdMap[test.Send.Method].Param
434 test.Send.Expect = v.Send.Expect
435 test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
sslobodrd9daabf2019-02-05 13:14:21 -0500436 for _,v1 := range v.Send.MetaData {
437 test.Send.MetaData = append(test.Send.MetaData,v1)
438 }
439 for _,v1 := range v.Send.ExpectMeta {
440 test.Send.ExpectMeta = append(test.Send.ExpectMeta,v1)
441 }
sslobodrd6e07e72019-01-31 16:07:20 -0500442 for _,v1 := range v.Servers {
443 var srvr Server
444 if serverExists(v1.Name, ts) == false {
445 log.Errorf("Server '%s' is not defined!!", v1.Name)
446 return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
447 }
448 srvr.Name = v1.Name
449 srvr.Meta = v1.Meta
450 test.Srvr = append(test.Srvr, srvr)
451 }
452 tc.Tests = append(tc.Tests, test)
453 }
454 if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
455 if err := t.ExecuteTemplate(f, "runTests.go", tc); err != nil {
456 log.Errorf("Unable to execute template for runTests.go: %v", err)
457 }
458 f.Close()
459 } else {
460 log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
461 }
462 return nil
463}
464
465func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
466
467 // Create a directory for the tests
468 if err := os.Mkdir(srcDir, 0777); err != nil {
469 log.Errorf("Unable to create directory 'tests':%v\n", err)
470 return err
471 }
472
473 for k,v := range conf.Suites {
474 var suiteDir string = srcDir+"/"+v
475 log.Debugf("Suite[%d] - %s", k, v)
476 ts := &TestSuite{}
477 ts.loadSuite(v)
478 log.Debugf("Suite %s: %v", v, ts)
479 log.Info("Processing test suite %s", v)
480
481 t := template.Must(template.New("").ParseFiles("../templates/server.go",
482 "../templates/serverInit.go",
483 "../templates/client.go",
484 "../templates/clientInit.go",
485 "../templates/runTests.go",
486 "../templates/main.go"))
487 // Create a directory for he source code for this test suite
488 if err := os.Mkdir(suiteDir, 0777); err != nil {
489 log.Errorf("Unable to create directory '%s':%v\n", v, err)
490 return err
491 }
492 // Generate the server source files
493 if err := generateServers(conf, suiteDir, ts, t); err != nil {
494 log.Errorf("Unable to generate server source files: %v", err)
495 return err
496 }
497 // Generate the client source files
498 if err := generateClients(conf, suiteDir, ts, t); err != nil {
499 log.Errorf("Unable to generate client source files: %v", err)
500 return err
501 }
502 // Generate the test case source file
503 if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
504 log.Errorf("Unable to generate test case source file: %v", err)
505 return err
506 }
507
508 // Finally generate the main file
509 log.Info("Generating main.go")
510 if f,err := os.Create(suiteDir+"/main.go"); err == nil {
511 if err := t.ExecuteTemplate(f, "main.go", ts.Env); err != nil {
512 log.Errorf("Unable to execute template for main.go: %v", err)
513 }
514 f.Close()
515 } else {
516 log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
517 }
518
519 log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
520 if err := os.Chdir(suiteDir); err != nil {
521 log.Errorf("Could not change to directory '%s':%v",suiteDir, err)
522 }
523 cmd := exec.Command("go", "build", "-o", outDir+"/"+v[:len(v)-5])
524 cmd.Stdin = os.Stdin
525 cmd.Stdout = os.Stdout
526 cmd.Stderr = os.Stderr
527 if err := cmd.Run(); err != nil {
528 log.Errorf("Error running the compile command:%v", err)
529 }
530 if err := os.Chdir("../../suites"); err != nil {
531 log.Errorf("Could not change to directory '%s':%v","../../suites", err)
532 }
533
534 // Now generate the test cases
535
536 }
537 return nil
538
539}
540
541func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
542 // Generate the main test driver file
543 if err := os.Mkdir(srcDir, 0777); err != nil {
544 log.Errorf("Unable to create directory 'driver':%v\n", err)
545 return err
546 }
547 t := template.Must(template.New("").ParseFiles("../templates/runAll.go"))
548 if f,err := os.Create(srcDir+"/runAll.go"); err == nil {
549 if err := t.ExecuteTemplate(f, "runAll.go", conf.Suites); err != nil {
550 log.Errorf("Unable to execute template for main.go: %v", err)
551 }
552 f.Close()
553 } else {
554 log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
555 }
556
557 // Compile the test driver file
558 log.Info("Compiling the test driver")
559 if err := os.Chdir("../tests/driver"); err != nil {
560 log.Errorf("Could not change to directory 'driver':%v", err)
561 }
562 cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
563 cmd.Stdin = os.Stdin
564 cmd.Stdout = os.Stdout
565 cmd.Stderr = os.Stderr
566 if err := cmd.Run(); err != nil {
567 log.Errorf("Error running the compile command:%v", err)
568 }
569 if err := os.Chdir("../../suites"); err != nil {
570 log.Errorf("Could not change to directory 'driver':%v",err)
571 }
572
573 return nil
574}
575
576
577func main() {
578
579 conf,err := parseCmd()
580 if err != nil {
581 fmt.Printf("Error: %v\n", err)
582 return
583 }
584
585 // Setup logging
586 if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
587 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
588 }
589
590 defer log.CleanUp()
591
592 // Parse the config file
593 if err := conf.loadConfig(); err != nil {
594 log.Error(err)
595 }
596
597 generateTestSuites(conf, "../tests", "/src/tests")
598 generateTestDriver(conf, "../tests/driver", "/src/tests")
599 return
600}
601