blob: ce40acb2f9fc5725bb49945b357f2ad5e0dd1a35 [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package afrouter
import (
"errors"
"fmt"
"github.com/opencord/voltha-lib-go/v2/pkg/log"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
type BindingRouter struct {
name string
association associationType
//routingField string
grpcService string
//protoDescriptor *pb.FileDescriptorSet
//methodMap map[string]byte
beCluster *cluster
bindings map[string]*backend
bindingType string
bindingField string
bindingMethod string
currentBackend **backend
}
func (br BindingRouter) IsStreaming(_ string) (bool, bool) {
panic("not implemented")
}
func (br BindingRouter) BackendCluster(s string, metaKey string) (*cluster, error) {
return br.beCluster, nil
//return nil,errors.New("Not implemented yet")
}
func (br BindingRouter) Name() string {
return br.name
}
func (br BindingRouter) Service() string {
return br.grpcService
}
func (br BindingRouter) GetMetaKeyVal(serverStream grpc.ServerStream) (string, string, error) {
var rtrnK = ""
var rtrnV = ""
// Get the metadata from the server stream
md, ok := metadata.FromIncomingContext(serverStream.Context())
if !ok {
return rtrnK, rtrnV, errors.New("Could not get a server stream metadata")
}
// Determine if one of the method routing keys exists in the metadata
if _, ok := md[br.bindingField]; ok {
rtrnV = md[br.bindingField][0]
rtrnK = br.bindingField
}
return rtrnK, rtrnV, nil
}
func (br BindingRouter) FindBackendCluster(becName string) *cluster {
if becName == br.beCluster.name {
return br.beCluster
}
return nil
}
func (br BindingRouter) ReplyHandler(v interface{}) error {
return nil
}
func (br BindingRouter) Route(sel interface{}) (*backend, *connection) {
var err error
switch sl := sel.(type) {
case *requestFrame:
if b, ok := br.bindings[sl.metaVal]; ok { // binding exists, just return it
return b, nil
} else { // establish a new binding or error.
if sl.metaVal != "" {
err = fmt.Errorf("Attempt to route on non-existent metadata value '%s' in key '%s'",
sl.metaVal, sl.metaKey)
log.Error(err)
sl.err = err
return nil, nil
}
if sl.methodInfo.method != br.bindingMethod {
err = fmt.Errorf("Binding must occur with method %s but attempted with method %s",
br.bindingMethod, sl.methodInfo.method)
log.Error(err)
sl.err = err
return nil, nil
}
log.Debugf("MUST CREATE A NEW BINDING MAP ENTRY!!")
if *br.currentBackend, err = br.beCluster.nextBackend(*br.currentBackend, BackendSequenceRoundRobin); err == nil {
// Use the name of the backend as the metaVal for this new binding
br.bindings[(*br.currentBackend).name] = *br.currentBackend
return *br.currentBackend, nil
} else {
log.Error(err)
sl.err = err
return nil, nil
}
}
default:
return nil, nil
}
}
func newBindingRouter(rconf *RouterConfig, config *RouteConfig) (Router, error) {
var rtrn_err = false
var err error
log.Debugf("Creating binding router %s", config.Name)
// A name must exist
if config.Name == "" {
log.Error("A router 'name' must be specified")
rtrn_err = true
}
if rconf.ProtoPackage == "" {
log.Error("A 'package' must be specified")
rtrn_err = true
}
if rconf.ProtoService == "" {
log.Error("A 'service' must be specified")
rtrn_err = true
}
//if config.RouteField == "" {
// log.Error("A 'routing_field' must be specified")
// rtrn_err = true
//}
// TODO: Using the specified service, the imported proto
// descriptor file should be scanned for all methods provided
// for this router to ensure that this field exists in
// the message(s) passed to the method. This will avoid run
// time failures that might not be detected for long periods
// of time.
// TODO The routes section is currently not being used
// so the router will route all methods based on the
// routing_field. This needs to be done.
var bptr *backend
br := BindingRouter{
name: config.Name,
grpcService: rconf.ProtoService,
bindings: make(map[string]*backend),
//methodMap:make(map[string]byte),
currentBackend: &bptr,
}
// A binding association must exist
br.association = config.Binding.Association
if br.association == AssociationUndefined {
log.Error("An binding association must be specified")
rtrn_err = true
}
// A binding type must exist
// TODO: This is parsed but ignored and a header based type is used.
if config.Binding.Type != "header" {
log.Error("The binding type must be set to header")
rtrn_err = true
} else {
br.bindingType = config.Binding.Type
}
// A binding method must exist
if config.Binding.Method == "" {
log.Error("The binding method must be specified")
rtrn_err = true
} else {
br.bindingMethod = config.Binding.Method
}
// A binding field must exxist
if config.Binding.Field == "" {
log.Error("The binding field must be specified")
rtrn_err = true
} else {
br.bindingField = config.Binding.Field
}
// Create the backend cluster or link to an existing one
var ok bool
if br.beCluster, ok = clusters[config.backendCluster.Name]; !ok {
if br.beCluster, err = newBackendCluster(config.backendCluster); err != nil {
log.Errorf("Could not create a backend for router %s", config.Name)
rtrn_err = true
}
}
// HERE HERE HERE
if rtrn_err {
return br, fmt.Errorf("Failed to create a new router '%s'", br.name)
}
return br, nil
}