blob: c9bef79f31c4be2b2bd57461ed9ad4730804ba77 [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
// Command line parameters and parsing
import (
"encoding/json"
"errors"
"flag"
"fmt"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/opencord/voltha-go/common/log"
"io/ioutil"
"os"
"path"
)
const (
default_InstanceID = "arouter001"
)
func ParseCmd() (*Configuration, error) {
config := &Configuration{}
cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError)
config.ConfigFile = cmdParse.String("config", "arouter.json", "The configuration file for the affinity router")
config.LogLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router")
config.GrpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
config.DisplayVersionOnly = cmdParse.Bool("version", false, "Print version information and exit")
config.DryRun = cmdParse.Bool("dry-run", false, "Verify config file, but exit before starting to serve requests")
err := cmdParse.Parse(os.Args[1:])
if err != nil {
//return err
return nil, errors.New("Error parsing the command line")
}
//if(!cmdParse.Parsed()) {
//}
if val, have := os.LookupEnv("HOSTNAME"); have {
config.InstanceID = val
} else {
config.InstanceID = default_InstanceID
}
return config, nil
}
// Configuration file loading and parsing
type Configuration struct {
InstanceID string
ConfigFile *string
LogLevel *int
GrpcLog *bool
DisplayVersionOnly *bool
DryRun *bool
Servers []ServerConfig `json:"servers"`
Ports PortConfig `json:"ports"`
ServerCertificates ServerCertConfig `json:"serverCertificates"`
ClientCertificates ClientCertConfig `json:"clientCertificates"`
BackendClusters []BackendClusterConfig `json:"backend_clusters"`
Routers []RouterConfig `json:"routers"`
Api ApiConfig
}
type RouterConfig struct {
Name string `json:"name"`
ProtoService string `json:"service"`
ProtoPackage string `json:"package"`
ProtoFile string `json:"proto_descriptor"`
Routes []RouteConfig `json:"routes"`
protoDescriptor descriptor.FileDescriptorSet
}
type RouteConfig struct {
Name string `json:"name"`
Type routeType `json:"type"`
Association associationType `json:"association"`
RouteField string `json:"routing_field"`
Methods []string `json:"methods"` // The GRPC methods to route using the route field
NbBindingMethods []string `json:"nb_binding_methods"`
BackendCluster string `json:"backend_cluster"`
Binding BindingConfig `json:"binding"`
Overrides []OverrideConfig `json:"overrides"`
backendCluster *BackendClusterConfig
}
type BindingConfig struct {
Type string `json:"type"`
Field string `json:"field"`
Method string `json:"method"`
Association associationType `json:"association"`
}
type OverrideConfig struct {
Methods []string `json:"methods"`
Method string `json:"method"`
RouteField string `json:"routing_field"`
}
// Backend configuration
type BackendClusterConfig struct {
Name string `json:"name"`
Backends []BackendConfig `json:"backends"`
}
type BackendConfig struct {
Name string `json:"name"`
Type backendType `json:"type"`
Association AssociationConfig `json:"association"`
Connections []ConnectionConfig `json:"connections"`
}
type AssociationConfig struct {
Strategy associationStrategy `json:"strategy"`
Location associationLocation `json:"location"`
Field string `json:"field"`
Key string `json:"key"`
}
type ConnectionConfig struct {
Name string `json:"name"`
Addr string `json:"addr"`
Port string `json:"port"`
}
// Server configuration
type ServerConfig struct {
Name string `json:"name"`
Port uint `json:"port"`
Addr string `json:"address"`
Type string `json:"type"`
Routers []RouterPackage `json:"routers"`
routers map[string]*RouterConfig
}
type RouterPackage struct {
Router string `json:"router"`
Package string `json:"package"`
}
// Port configuration
type PortConfig struct {
GrpcPort uint `json:"grpcPort"`
StreamingGrpcPort uint `json:"streamingGrpcPort"`
TlsGrpcPort uint `json:"tlsGrpcPort"`
TlsStreamingGrpcPort uint `json:"tlsStreamingGrpcPort"`
ControlPort uint `json:"controlPort"`
}
// Server Certificate configuration
type ServerCertConfig struct {
GrpcCert string `json:"grpcCertificate"` // File path to the certificate file
GrpcKey string `json:"grpcKey"` // File path to the key file
GrpcCsr string `json:"grpcCsr"` // File path to the CSR file
}
// Client Certificate configuration
type ClientCertConfig struct {
GrpcCert string `json:"grpcCertificate"` // File path to the certificate file
GrpcKey string `json:"grpcKey"` // File path to the key file
GrpcCsr string `json:"grpcCsr"` // File path to the CSR file
}
// Api configuration
type ApiConfig struct {
Addr string `json:"address"`
Port uint `json:"port"`
}
func (conf *Configuration) LoadConfig() error {
configF, err := os.Open(*conf.ConfigFile)
log.Info("Loading configuration from: ", *conf.ConfigFile)
if err != nil {
log.Error(err)
return err
}
defer configF.Close()
configBytes, err := ioutil.ReadAll(configF)
if err != nil {
log.Error(err)
return err
}
if err := json.Unmarshal(configBytes, conf); err != nil {
log.Errorf("Unmarshaling of the configuratino file failed: %v", err)
return err
}
// Now resolve references to different config objects in the
// config file. Currently there are 2 possible references
// to resolve: referecnes to routers in the servers, and
// references to backend_cluster in the routers.
// Resolve router references for the servers
log.Debug("Resolving references in the config file")
for k := range conf.Servers {
//s.routers =make(map[string]*RouterConfig)
conf.Servers[k].routers = make(map[string]*RouterConfig)
for _, rPkg := range conf.Servers[k].Routers {
var found = false
// Locate the router "r" in the top lever Routers array
log.Debugf("Resolving router reference to router '%s' from server '%s'", rPkg.Router, conf.Servers[k].Name)
for rk := range conf.Routers {
if conf.Routers[rk].Name == rPkg.Router && !found {
log.Debugf("Reference to router '%s' found for package '%s'", rPkg.Router, rPkg.Package)
conf.Servers[k].routers[rPkg.Package] = &conf.Routers[rk]
found = true
} else if conf.Routers[rk].Name == rPkg.Router && found {
if _, ok := conf.Servers[k].routers[rPkg.Package]; !ok {
log.Debugf("Reference to router '%s' found for package '%s'", rPkg.Router, rPkg.Package)
conf.Servers[k].routers[rPkg.Package] = &conf.Routers[rk]
} else {
err := fmt.Errorf("Duplicate router '%s' defined for package '%s'", rPkg.Router, rPkg.Package)
log.Error(err)
return err
}
}
}
if !found {
err := fmt.Errorf("Router %s for server %s not found in config", conf.Servers[k].Name, rPkg.Router)
log.Error(err)
return err
}
}
}
// Resolve backend references for the routers
for rk, rv := range conf.Routers {
for rtk, rtv := range rv.Routes {
var found = false
log.Debugf("Resolving backend reference to %s from router %s", rtv.BackendCluster, rv.Name)
for bek, bev := range conf.BackendClusters {
log.Debugf("Checking cluster %s", conf.BackendClusters[bek].Name)
if rtv.BackendCluster == bev.Name && !found {
conf.Routers[rk].Routes[rtk].backendCluster = &conf.BackendClusters[bek]
found = true
} else if rtv.BackendCluster == bev.Name && found {
err := fmt.Errorf("Duplicate backend defined, %s", conf.BackendClusters[bek].Name)
log.Error(err)
return err
}
}
if !found {
err := fmt.Errorf("Backend %s for router %s not found in config",
rtv.BackendCluster, rv.Name)
log.Error(err)
return err
}
}
}
return nil
}