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