blob: 41cd203bbb47cbd4a63eaa62e613208652c0874e [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
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
17package afrouter
18
19import (
20 "errors"
21 "fmt"
22 "github.com/golang/protobuf/proto"
23 "github.com/opencord/voltha-go/common/log"
24 "google.golang.org/grpc"
25 "google.golang.org/grpc/metadata"
26 "io/ioutil"
27)
28
29const NoMeta = "nometa"
30
31type MethodRouter struct {
32 name string
33 service string
34 methodRouter map[string]map[string]Router // map of [metadata][method]
35 methodStreaming map[string]streamingDirections
36}
37
38type streamingDirections struct {
39 request bool
40 response bool
41}
42
43func newMethodRouter(config *RouterConfig) (Router, error) {
44 // Load the protobuf descriptor file
45 fb, err := ioutil.ReadFile(config.ProtoFile)
46 if err != nil {
47 log.Errorf("Could not open proto file '%s'", config.ProtoFile)
48 return nil, err
49 }
50 if err := proto.Unmarshal(fb, &config.protoDescriptor); err != nil {
51 log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
52 return nil, err
53 }
54
55 mr := MethodRouter{
56 name: config.Name,
57 service: config.ProtoService,
58 methodRouter: map[string]map[string]Router{
59 NoMeta: make(map[string]Router), // For routes not needing metadata (all except binding at this time)
60 },
61 methodStreaming: make(map[string]streamingDirections),
62 }
63 log.Debugf("Processing MethodRouter config %v", *config)
64
65 for _, file := range config.protoDescriptor.File {
66 if *file.Package == config.ProtoPackage {
67 for _, service := range file.Service {
68 if *service.Name == config.ProtoService {
69 for _, method := range service.Method {
70 if clientStreaming, serverStreaming := method.ClientStreaming != nil && *method.ClientStreaming, method.ServerStreaming != nil && *method.ServerStreaming; clientStreaming || serverStreaming {
71 mr.methodStreaming[*method.Name] = streamingDirections{
72 request: clientStreaming,
73 response: serverStreaming,
74 }
75 }
76 }
77 }
78 }
79 }
80 }
81
82 if len(config.Routes) == 0 {
Scott Baker4989fe92019-10-09 17:03:06 -070083 return nil, fmt.Errorf("Router %s must have at least one route", config.Name)
Scott Bakere7144bc2019-10-01 14:16:47 -070084 }
85 for _, rtv := range config.Routes {
86 //log.Debugf("Processing route: %v",rtv)
87 var idx1 string
88 r, err := newSubRouter(config, &rtv)
89 if err != nil {
90 return nil, err
91 }
92 if rtv.Type == RouteTypeBinding {
93 idx1 = rtv.Binding.Field
94 if _, ok := mr.methodRouter[idx1]; !ok { // /First attempt on this key
95 mr.methodRouter[idx1] = make(map[string]Router)
96 }
97 } else {
98 idx1 = NoMeta
99 }
100 switch len(rtv.Methods) {
101 case 0:
Scott Baker4989fe92019-10-09 17:03:06 -0700102 return nil, fmt.Errorf("Route for router %s must have at least one method", config.Name)
Scott Bakere7144bc2019-10-01 14:16:47 -0700103 case 1:
104 if rtv.Methods[0] == "*" {
105 return r, nil
106 } else {
107 log.Debugf("Setting router '%s' for single method '%s'", r.Name(), rtv.Methods[0])
108 if _, ok := mr.methodRouter[idx1][rtv.Methods[0]]; !ok {
109 mr.methodRouter[idx1][rtv.Methods[0]] = r
110 } else {
Scott Baker4989fe92019-10-09 17:03:06 -0700111 err := fmt.Errorf("Attempt to define method %s for 2 routes: %s & %s", rtv.Methods[0],
112 r.Name(), mr.methodRouter[idx1][rtv.Methods[0]].Name())
Scott Bakere7144bc2019-10-01 14:16:47 -0700113 log.Error(err)
114 return mr, err
115 }
116 }
117 default:
118 for _, m := range rtv.Methods {
119 log.Debugf("Processing Method %s", m)
120 if _, ok := mr.methodRouter[idx1][m]; !ok {
121 log.Debugf("Setting router '%s' for method '%s'", r.Name(), m)
122 mr.methodRouter[idx1][m] = r
123 } else {
Scott Baker4989fe92019-10-09 17:03:06 -0700124 err := fmt.Errorf("Attempt to define method %s for 2 routes: %s & %s", m, r.Name(), mr.methodRouter[idx1][m].Name())
Scott Bakere7144bc2019-10-01 14:16:47 -0700125 log.Error(err)
126 return mr, err
127 }
128 }
129 }
130 }
131
132 return mr, nil
133}
134
135func (mr MethodRouter) Name() string {
136 return mr.name
137}
138
139func (mr MethodRouter) Service() string {
140 return mr.service
141}
142
143func (mr MethodRouter) GetMetaKeyVal(serverStream grpc.ServerStream) (string, string, error) {
144 var rtrnK = NoMeta
145 var rtrnV = ""
146
147 // Get the metadata from the server stream
148 md, ok := metadata.FromIncomingContext(serverStream.Context())
149 if !ok {
150 return rtrnK, rtrnV, errors.New("Could not get a server stream metadata")
151 }
152
153 // Determine if one of the method routing keys exists in the metadata
154 for k := range mr.methodRouter {
155 if _, ok := md[k]; ok {
156 rtrnV = md[k][0]
157 rtrnK = k
158 break
159 }
160 }
161 return rtrnK, rtrnV, nil
162
163}
164
165func (mr MethodRouter) ReplyHandler(sel interface{}) error {
166 switch sl := sel.(type) {
167 case *responseFrame:
168 if r, ok := mr.methodRouter[NoMeta][sl.method]; ok {
169 return r.ReplyHandler(sel)
170 }
171 return errors.New("MethodRouter.ReplyHandler called with unknown meta or method")
172 default:
173 return errors.New("MethodRouter.ReplyHandler called with non-reponseFrame")
174 }
175}
176
177func (mr MethodRouter) Route(sel interface{}) (*backend, *connection) {
178 switch sl := sel.(type) {
179 case *requestFrame:
180 if r, ok := mr.methodRouter[sl.metaKey][sl.methodInfo.method]; ok {
181 return r.Route(sel)
182 }
183 sl.err = fmt.Errorf("MethodRouter.Route unable to resolve meta %s, method %s", sl.metaKey, sl.methodInfo.method)
184 log.Error(sl.err)
185 return nil, nil
186 default:
187 log.Errorf("Internal: invalid data type in Route call %v", sel)
188 return nil, nil
189 }
190}
191
192func (mr MethodRouter) IsStreaming(method string) (bool, bool) {
193 streamingDirections := mr.methodStreaming[method]
194 return streamingDirections.request, streamingDirections.response
195}
196
197func (mr MethodRouter) BackendCluster(mthd string, metaKey string) (*cluster, error) {
198 if r, ok := mr.methodRouter[metaKey][mthd]; ok {
199 return r.BackendCluster(mthd, metaKey)
200 }
Scott Baker4989fe92019-10-09 17:03:06 -0700201 err := fmt.Errorf("No backend cluster exists for method '%s' using meta key '%s'", mthd, metaKey)
Scott Bakere7144bc2019-10-01 14:16:47 -0700202 log.Error(err)
203 return nil, err
204}
205
206func (mr MethodRouter) FindBackendCluster(beName string) *cluster {
207 for _, meta := range mr.methodRouter {
208 for _, r := range meta {
209 if rtrn := r.FindBackendCluster(beName); rtrn != nil {
210 return rtrn
211 }
212 }
213 }
214 return nil
215}