This is an automated email from the ASF dual-hosted git repository.

rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 000c1b9133 Convert Traffic Ops into a Service Oriented 
architecture(SOA) product (#6754)
000c1b9133 is described below

commit 000c1b913317f3936f8ed3491030ed17052fcbba
Author: Srijeet Chatterjee <[email protected]>
AuthorDate: Tue May 10 08:57:46 2022 -0600

    Convert Traffic Ops into a Service Oriented architecture(SOA) product 
(#6754)
    
    * initial changes
    
    * working changes
    
    * formatting and cleanup
    
    * cleanup
    
    * adding log handlers
    
    * add dcos, cleanup
    
    * add todos
    
    * add todos
    
    * cleanup config
    
    * add insecure option to backend config
    
    * Adding proper log handlers
    
    * remove priv level from backend config
    
    * code review first pass
    
    * code review fixes final
    
    * change log call
    
    * change mutex type
    
    * addressing code review comments
    
    * change regex handling
---
 CHANGELOG.md                                       |   1 +
 docs/source/admin/traffic_ops.rst                  |  34 +++++-
 traffic_ops/app/conf/backends.conf                 |  52 +++++++++
 traffic_ops/app/conf/production/backends.conf      |   3 +
 traffic_ops/etc/init.d/traffic_ops                 |   2 +-
 traffic_ops/traffic_ops_golang/config/config.go    |  54 +++++++++
 .../routing/middleware/wrappers.go                 |  10 +-
 traffic_ops/traffic_ops_golang/routing/routing.go  | 123 ++++++++++++++++++++-
 .../traffic_ops_golang/traffic_ops_golang.go       |  40 ++++++-
 9 files changed, 308 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6c302f728..7c6565a933 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Replaces all Traffic Portal Tenant select boxes with a novel tree select box 
[#6427](https://github.com/apache/trafficcontrol/issues/6427).
 - Traffic Monitor: Add support for `access.log` to TM.
 - Added functionality for login to provide a Bearer token and for that token 
to be later used for authorization.
+- [Traffic Ops] Added support for backend configurations so that Traffic Ops 
can act as a reverse proxy for these services 
[#6754](https://github.com/apache/trafficcontrol/pull/6754).
 - Added functionality for CDN locks, so that they can be shared amongst a list 
of specified usernames. 
 - [Traffic Ops | Traffic Go Clients | T3C] Add additional timestamp fields to 
server for queuing and dequeueing config and revalidate updates.
 - Added layered profile feature to 4.0 for `GET` /servers/, `POST` /servers/, 
`PUT` /servers/{id} and `DELETE` /servers/{id}.
diff --git a/docs/source/admin/traffic_ops.rst 
b/docs/source/admin/traffic_ops.rst
index d0b1d84120..6ea3cbf479 100644
--- a/docs/source/admin/traffic_ops.rst
+++ b/docs/source/admin/traffic_ops.rst
@@ -19,7 +19,7 @@
 ***********
 Traffic Ops
 ***********
-Traffic Ops is quite possible the single most complex and most important 
Traffic Control component. It has many different configuration options that 
affect a wide range of other components and their interactions.
+Traffic Ops is quite possibly the single most complex and most important 
Traffic Control component. It has many different configuration options that 
affect a wide range of other components and their interactions.
 
 .. _to-install:
 
@@ -257,7 +257,7 @@ While this section contains instructions for running 
Traffic Ops manually, the o
 
 traffic_ops_golang
 ------------------
-``traffic_ops_golang [--version] [--plugins] [--api-routes] --cfg CONFIG_PATH 
--dbcfg DB_CONFIG_PATH [--riakcfg RIAK_CONFIG_PATH]``
+``traffic_ops_golang [--version] [--plugins] [--api-routes] --cfg CONFIG_PATH 
--dbcfg DB_CONFIG_PATH [--riakcfg RIAK_CONFIG_PATH] [--backendcfg 
BACKEND_CONFIG_PATH]``
 
 .. option:: --cfg CONFIG_PATH
 
@@ -282,6 +282,10 @@ traffic_ops_golang
 
        .. impl-detail:: The name of this flag is derived from the current 
database used in the implementation of Traffic Vault - `Riak KV 
<https://riak.com/products/riak-kv/index.html>`_.
 
+.. option:: --backendcfg BACKEND_CONFIG_PATH
+
+       This optional command line flag specifies the absolute or relative path 
to a configuration file used by Traffic Ops to act as a reverse proxy and 
forward requests on the specified paths to the corresponding hosts - 
`backends.conf`_
+
 .. option:: --version
 
        Print version information and exit.
@@ -586,6 +590,32 @@ This file sets authentication options for connections to 
Riak when used as the T
 
 .. impl-detail:: The name of this file is derived from the current database 
used in the implementation of Traffic Vault - `Riak KV 
<https://riak.com/products/riak-kv/index.html>`_.
 
+backends.conf
+"""""""""""""
+This file deals with the configuration parameters of running Traffic Ops as a 
reverse proxy for certain endpoints that need to be served externally by other 
backend services. It is a JSON-format set of options and their respective 
values. `traffic_ops_golang`_ will use whatever file is specified (if any) by 
its :option:`--backendcfg` option. The keys of the file are described below.
+
+:routes: This is an array of options to configure Traffic Ops to forward 
requests of specified types to the appropriate backends.
+
+       :path:              The regex matching the endpoint that will be served 
by the backend, for example, :regexp:`^/api/4.0/foo?$`.
+       :method:            The HTTP method for the above mentioned path, for 
example, ``GET`` or ``PUT``.
+       :routeId:           The integral identifier for the new route being 
added.
+       :hosts:             An array of the host object, which specifies the 
protocol, hostname and port where the request (if matched) needs to be 
forwarded to.
+
+               :protocol:     The protocol/scheme to be followed while 
forwarding the requests to the backend service.
+               :hostname:     The hostname of the server where the backend 
service is running.
+               :port:         The port (integer) on the backend server where 
the service is running.
+
+       :insecure:          A boolean specifying whether or not TO should 
verify the backend server's certificate chain and host name. This is not 
recommended for production use. This is an optional parameter, defaulting to 
``false`` when not present.
+       :permissions:       An array of permissions (strings) specifying the 
permissions required by the user to use this API route.
+       :opts:              A collection of key value pairs to control how the 
requests should be forwarded/ handled, for example, ``"alg": "roundrobin"``. 
Currently, only ``roundrobin`` is supported (which is also the default if 
nothing is specified) by Traffic Ops.
+
+Example backends.conf
+'''''''''''''''''''''
+.. include:: ../../../traffic_ops/app/conf/backends.conf
+       :code: json
+       :tab-width: 4
+
+
 Installing the SSL Certificate
 ------------------------------
 By default, Traffic Ops runs as an SSL web server (that is, over HTTPS), and a 
certificate needs to be installed.
diff --git a/traffic_ops/app/conf/backends.conf 
b/traffic_ops/app/conf/backends.conf
new file mode 100644
index 0000000000..2401a44996
--- /dev/null
+++ b/traffic_ops/app/conf/backends.conf
@@ -0,0 +1,52 @@
+{
+  "routes": [
+    {
+      "path": "^/api/4.0/foo?$",
+      "method": "GET",
+      "hosts": [
+        {
+          "protocol": "https",
+          "hostname": "localhost",
+          "port": 8444
+        },
+        {
+          "protocol": "https",
+          "hostname": "localhost",
+          "port": 8445
+        }
+      ],
+      "insecure": true,
+      "permissions": [
+        "CDN:READ"
+      ],
+      "routeId": 123456,
+      "opts": {
+        "alg": "roundrobin"
+      }
+    },
+    {
+      "path": "^/api/4.0/foos?$",
+      "method": "GET",
+      "hosts": [
+        {
+          "protocol": "https",
+          "hostname": "localhost",
+          "port": 8444
+        },
+        {
+          "protocol": "https",
+          "hostname": "localhost",
+          "port": 8445
+        }
+      ],
+      "insecure": true,
+      "permissions": [
+        "CDN:READ"
+      ],
+      "routeId": 123457,
+      "opts": {
+        "alg": "roundrobin"
+      }
+    }
+  ]
+}
diff --git a/traffic_ops/app/conf/production/backends.conf 
b/traffic_ops/app/conf/production/backends.conf
new file mode 100644
index 0000000000..4feaf1db51
--- /dev/null
+++ b/traffic_ops/app/conf/production/backends.conf
@@ -0,0 +1,3 @@
+{
+       "routes": [],
+}
diff --git a/traffic_ops/etc/init.d/traffic_ops 
b/traffic_ops/etc/init.d/traffic_ops
index d2fad881b1..12674c981b 100755
--- a/traffic_ops/etc/init.d/traffic_ops
+++ b/traffic_ops/etc/init.d/traffic_ops
@@ -50,7 +50,7 @@ start ()
        stop
        echo -e "Starting Traffic Ops\n"
        ulimit -n 200000 || echo "Setting ulimit max files failed for 
traffic_ops_golang"
-       cd $TO_DIR && $TO_DIR/bin/traffic_ops_golang -cfg $TO_DIR/conf/cdn.conf 
-dbcfg $TO_DIR/conf/production/database.conf -riakcfg 
$TO_DIR/conf/production/riak.conf &
+       cd $TO_DIR && $TO_DIR/bin/traffic_ops_golang -cfg $TO_DIR/conf/cdn.conf 
-dbcfg $TO_DIR/conf/production/database.conf -riakcfg 
$TO_DIR/conf/production/riak.conf -backendcfg 
$TO_DIR/conf/production/backends.conf &
 }
 
 stop ()
diff --git a/traffic_ops/traffic_ops_golang/config/config.go 
b/traffic_ops/traffic_ops_golang/config/config.go
index f9680c10a3..521da1d9bd 100644
--- a/traffic_ops/traffic_ops_golang/config/config.go
+++ b/traffic_ops/traffic_ops_golang/config/config.go
@@ -36,6 +36,35 @@ import (
        "github.com/apache/trafficcontrol/lib/go-util"
 )
 
+// Options is a structure used to hold the route configuration options that 
can be supplied for the backend routes.
+type Options struct {
+       Algorithm string `json:"alg"`
+}
+
+// Host is a structure that holds the host info for the backend route.
+type Host struct {
+       Protocol string `json:"protocol"`
+       Hostname string `json:"hostname"`
+       Port     int    `json:"port"`
+}
+
+// BackendRoute holds all the information about a configured route, for which 
Traffic Ops serves as a reverse proxy.
+type BackendRoute struct {
+       Path        string   `json:"path"`
+       Method      string   `json:"method"`
+       Hosts       []Host   `json:"hosts"`
+       Opts        Options  `json:"opts"`
+       ID          int      `json:"routeId"`
+       Insecure    bool     `json:"insecure"`
+       Permissions []string `json:"permissions"`
+       Index       int
+}
+
+// BackendConfig is a structure that holds the configuration supplied to 
Traffic Ops, which makes it act as a reverse proxy to the specified routes.
+type BackendConfig struct {
+       Routes []BackendRoute `json:"routes"`
+}
+
 // Config reflects the structure of the cdn.conf file
 type Config struct {
        URL                         *url.URL `json:"-"`
@@ -286,6 +315,31 @@ func (c Config) EventLog() log.LogLocation {
 const BlockStartup = true
 const AllowStartup = false
 
+func LoadBackendConfig(backendConfigPath string) (BackendConfig, error) {
+       confBytes, err := ioutil.ReadFile(backendConfigPath)
+       if err != nil {
+               return BackendConfig{}, fmt.Errorf("reading backend conf '%s': 
%v", backendConfigPath, err)
+       }
+
+       cfg := BackendConfig{}
+       err = json.Unmarshal(confBytes, &cfg)
+       if err != nil {
+               return BackendConfig{}, fmt.Errorf("unmarshalling '%s': %v", 
backendConfigPath, err)
+       }
+       for _, r := range cfg.Routes {
+               if r.Opts.Algorithm != "" && r.Opts.Algorithm != "roundrobin" {
+                       return cfg, errors.New("algorithm can only be 
roundrobin or blank")
+               }
+               for _, h := range r.Hosts {
+                       rawURL := h.Protocol + "://" + h.Hostname + ":" + 
strconv.Itoa(h.Port)
+                       if _, err = url.ParseRequestURI(rawURL); err != nil {
+                               return cfg, fmt.Errorf("couldn't convert host 
info into a valid URI: %v", err)
+                       }
+               }
+       }
+       return cfg, nil
+}
+
 func LoadCdnConfig(cdnConfPath string) (Config, error) {
        // load json from cdn.conf
        confBytes, err := ioutil.ReadFile(cdnConfPath)
diff --git a/traffic_ops/traffic_ops_golang/routing/middleware/wrappers.go 
b/traffic_ops/traffic_ops_golang/routing/middleware/wrappers.go
index e307faadb9..57ed0bb520 100644
--- a/traffic_ops/traffic_ops_golang/routing/middleware/wrappers.go
+++ b/traffic_ops/traffic_ops_golang/routing/middleware/wrappers.go
@@ -212,7 +212,7 @@ func WrapAccessLog(secret string, h http.Handler) 
http.HandlerFunc {
                                        imsType = IMSMISS
                                }
                        }
-                       log.EventfRaw(`%s - %s [%s] "%v %v?%v %s" %v %v %v "%v" 
%v %s`, r.RemoteAddr, user, time.Now().Format(AccessLogTimeFormat), r.Method, 
r.URL.Path, r.URL.RawQuery, r.Proto, iw.Code, iw.ByteCount, 
int(time.Now().Sub(start)/time.Millisecond), r.UserAgent(), 
r.Header.Get(RouteID), imsType)
+                       log.EventfRaw(`%s - %s [%s] "%v %v?%v %s" %v %v %v "%v" 
%v %s`, r.RemoteAddr, user, time.Now().Format(AccessLogTimeFormat), r.Method, 
r.URL.Path, r.URL.RawQuery, r.Proto, iw.Code, iw.ByteCount, 
int(time.Now().Sub(start)/time.Millisecond), r.UserAgent(), 
r.Context().Value(RouteID), imsType)
                }()
                h.ServeHTTP(iw, r)
        }
@@ -276,6 +276,14 @@ func NotImplementedHandler() http.Handler {
        })
 }
 
+func BackendErrorHandler(code int, userErr error, sysErr error) http.Handler {
+       return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+               w.Header().Set(rfc.ContentType, rfc.ApplicationJSON)
+               w.WriteHeader(code)
+               api.HandleErr(w, r, nil, code, userErr, sysErr)
+       })
+}
+
 // DisabledRouteHandler returns a http.Handler which returns a HTTP 5xx code 
to the client, and an error message indicating the route is currently disabled.
 // This is used for routes which have been disabled via configuration. See 
config.ConfigTrafficOpsGolang.RoutingBlacklist.DisabledRoutes.
 func DisabledRouteHandler() http.Handler {
diff --git a/traffic_ops/traffic_ops_golang/routing/routing.go 
b/traffic_ops/traffic_ops_golang/routing/routing.go
index 3031e1c209..c3ca1adcb4 100644
--- a/traffic_ops/traffic_ops_golang/routing/routing.go
+++ b/traffic_ops/traffic_ops_golang/routing/routing.go
@@ -23,18 +23,23 @@ package routing
 
 import (
        "context"
+       "crypto/tls"
        "errors"
        "fmt"
        "net/http"
+       "net/http/httputil"
+       "net/url"
        "regexp"
        "sort"
        "strconv"
        "strings"
+       "sync"
        "sync/atomic"
        "time"
 
        "github.com/apache/trafficcontrol/lib/go-log"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+       "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/plugin"
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/routing/middleware"
@@ -46,6 +51,28 @@ import (
 // RoutePrefix is a prefix that all API routes must match.
 const RoutePrefix = "^api" // TODO config?
 
+type backendConfigSynced struct {
+       cfg config.BackendConfig
+       *sync.RWMutex
+}
+
+// backendCfg stores the current backend config supplied to traffic ops.
+var backendCfg = backendConfigSynced{RWMutex: &sync.RWMutex{}}
+
+// GetBackendConfig returns the current BackendConfig.
+func GetBackendConfig() config.BackendConfig {
+       backendCfg.RLock()
+       defer backendCfg.RUnlock()
+       return backendCfg.cfg
+}
+
+// SetBackendConfig sets the BackendConfig to the value supplied.
+func SetBackendConfig(backendConfig config.BackendConfig) {
+       backendCfg.Lock()
+       defer backendCfg.Unlock()
+       backendCfg.cfg = backendConfig
+}
+
 // A Route defines an association with a client request and a handler for that
 // request.
 type Route struct {
@@ -85,6 +112,7 @@ type ServerData struct {
        Profiling    *bool // Yes this is a field in the config but we want to 
live reload this value and NOT the entire config
        Plugins      plugin.Plugins
        TrafficVault trafficvault.TrafficVault
+       Mux          *http.ServeMux
 }
 
 // CompiledRoute ...
@@ -270,8 +298,8 @@ func Handler(
                }
 
                routeCtx := context.WithValue(ctx, api.PathParamsKey, params)
+               routeCtx = context.WithValue(routeCtx, middleware.RouteID, 
strconv.Itoa(compiledRoute.ID))
                r = r.WithContext(routeCtx)
-               r.Header.Add(middleware.RouteID, strconv.Itoa(compiledRoute.ID))
                compiledRoute.Handler(w, r)
                return
        }
@@ -280,8 +308,97 @@ func Handler(
                h.ServeHTTP(w, r)
                return
        }
+       var backendRouteHandled bool
+       backendConfig := GetBackendConfig()
+       for i, backendRoute := range backendConfig.Routes {
+               var params []string
+               routeParams := map[string]string{}
+               if backendRoute.Method == r.Method {
+                       for open := strings.Index(backendRoute.Path, "{"); open 
> 0; open = strings.Index(backendRoute.Path, "{") {
+                               close := strings.Index(backendRoute.Path, "}")
+                               if close < 0 {
+                                       panic("malformed route")
+                               }
+                               param := backendRoute.Path[open+1 : close]
+                               params = append(params, param)
+                               backendRoute.Path = backendRoute.Path[:open] + 
`([^/]+)` + backendRoute.Path[close+1:]
+                       }
+                       regex := regexp.MustCompile(backendRoute.Path)
+                       match := regex.FindStringSubmatch(r.URL.Path)
+                       if len(match) == 0 {
+                               continue
+                       }
+                       for i, v := range params {
+                               routeParams[v] = match[i+1]
+                       }
+                       if backendRoute.Opts.Algorithm == "" || 
backendRoute.Opts.Algorithm == "roundrobin" {
+                               index := backendRoute.Index % 
len(backendRoute.Hosts)
+                               host := backendRoute.Hosts[index]
+                               backendRoute.Index++
+                               backendConfig.Routes[i] = backendRoute
+                               backendRouteHandled = true
+                               rp := 
httputil.NewSingleHostReverseProxy(&url.URL{
+                                       Host:   host.Hostname + ":" + 
strconv.Itoa(host.Port),
+                                       Scheme: host.Protocol,
+                               })
+                               rp.Transport = &http.Transport{
+                                       TLSClientConfig: 
&tls.Config{InsecureSkipVerify: backendRoute.Insecure},
+                               }
+                               rp.ErrorHandler = func(w http.ResponseWriter, r 
*http.Request, err error) {
+                                       api.HandleErr(w, r, nil, 
http.StatusInternalServerError, nil, err)
+                                       return
+                               }
+                               routeCtx := context.WithValue(ctx, 
api.DBContextKey, db)
+                               routeCtx = context.WithValue(routeCtx, 
api.PathParamsKey, routeParams)
+                               routeCtx = context.WithValue(routeCtx, 
middleware.RouteID, strconv.Itoa(backendRoute.ID))
+                               r = r.WithContext(routeCtx)
+                               userErr, sysErr, code := 
HandleBackendRoute(cfg, backendRoute, w, r)
+                               if userErr != nil || sysErr != nil {
+                                       h2 := 
middleware.WrapAccessLog(cfg.Secrets[0], middleware.BackendErrorHandler(code, 
userErr, sysErr))
+                                       h2.ServeHTTP(w, r)
+                                       return
+                               }
+                               backendHandler := 
middleware.WrapAccessLog(cfg.Secrets[0], rp)
+                               backendHandler.ServeHTTP(w, r)
+                               return
+                       } else {
+                               h2 := middleware.WrapAccessLog(cfg.Secrets[0], 
middleware.BackendErrorHandler(http.StatusBadRequest, errors.New("only an 
algorithm of roundrobin is supported by the backend options currently"), nil))
+                               h2.ServeHTTP(w, r)
+                               return
+                       }
+               }
+       }
+       if !backendRouteHandled {
+               catchall.ServeHTTP(w, r)
+       }
+}
 
-       catchall.ServeHTTP(w, r)
+// HandleBackendRoute does all the pre processing for the backend routes.
+func HandleBackendRoute(cfg *config.Config, route config.BackendRoute, w 
http.ResponseWriter, r *http.Request) (error, error, int) {
+       var userErr, sysErr error
+       var errCode int
+       var user auth.CurrentUser
+       var inf *api.APIInfo
+
+       user, userErr, sysErr, errCode = api.GetUserFromReq(w, r, 
cfg.Secrets[0])
+       if userErr != nil || sysErr != nil {
+               return userErr, sysErr, errCode
+       }
+       if cfg.RoleBasedPermissions {
+               missingPerms := user.MissingPermissions(route.Permissions...)
+               if len(missingPerms) != 0 {
+                       msg := strings.Join(missingPerms, ", ")
+                       return fmt.Errorf("missing required Permissions: %s", 
msg), nil, http.StatusForbidden
+               }
+       }
+       api.AddUserToReq(r, user)
+       var params []string
+       inf, userErr, sysErr, errCode = api.NewInfo(r, params, nil)
+       if userErr != nil || sysErr != nil {
+               return userErr, sysErr, errCode
+       }
+       defer inf.Close()
+       return nil, nil, http.StatusOK
 }
 
 // IsRequestAPIAndUnknownVersion returns true if the request starts with 
`/api` and is a version not in the list of versions.
@@ -335,7 +452,7 @@ func RegisterRoutes(d ServerData) error {
 
        compiledRoutes := CompileRoutes(routes)
        getReqID := nextReqIDGetter()
-       http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+       d.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                Handler(compiledRoutes, versions, catchall, d.DB, &d.Config, 
getReqID, d.Plugins, d.TrafficVault, w, r)
        })
        return nil
diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go 
b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index a04c486d36..42a3acf45e 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -22,6 +22,7 @@ package main
 import (
        "crypto/tls"
        "encoding/json"
+       "errors"
        "flag"
        "fmt"
        "io/ioutil"
@@ -64,6 +65,7 @@ func main() {
        configFileName := flag.String("cfg", "", "The config file path")
        dbConfigFileName := flag.String("dbcfg", "", "The db config file path")
        riakConfigFileName := flag.String("riakcfg", "", "The riak config file 
path (DEPRECATED: use traffic_vault_backend = riak and traffic_vault_config in 
cdn.conf instead)")
+       backendConfigFileName := flag.String("backendcfg", "", "The backend 
config file path")
        flag.Parse()
 
        if *showVersion {
@@ -164,7 +166,18 @@ func main() {
                log.Errorln(debugServer.ListenAndServe())
        }()
 
-       if err := routing.RegisterRoutes(routing.ServerData{DB: db, Config: 
cfg, Profiling: &profiling, Plugins: plugins, TrafficVault: trafficVault}); err 
!= nil {
+       var backendConfig config.BackendConfig
+       if *backendConfigFileName != "" {
+               backendConfig, err = 
config.LoadBackendConfig(*backendConfigFileName)
+               routing.SetBackendConfig(backendConfig)
+               if err != nil {
+                       log.Errorf("error loading backend config: %v", err)
+               }
+       }
+
+       mux := http.NewServeMux()
+       d := routing.ServerData{DB: db, Config: cfg, Profiling: &profiling, 
Plugins: plugins, TrafficVault: trafficVault, Mux: mux}
+       if err := routing.RegisterRoutes(d); err != nil {
                log.Errorf("registering routes: %v\n", err)
                os.Exit(1)
        }
@@ -213,7 +226,7 @@ func main() {
                } else {
                        file.Close()
                }
-
+               server.Handler = mux
                if err := server.ListenAndServeTLS(cfg.CertPath, cfg.KeyPath); 
err != nil {
                        log.Errorf("stopping server: %v\n", err)
                        os.Exit(1)
@@ -232,10 +245,16 @@ func main() {
                continuousProfile(&profiling, &profilingLocation, cfg.Version)
        }
 
-       reloadProfilingConfig := func() {
+       reloadProfilingAndBackendConfig := func() {
                setNewProfilingInfo(*configFileName, &profiling, 
&profilingLocation, cfg.Version)
+               backendConfig, err = getNewBackendConfig(backendConfigFileName)
+               if err != nil {
+                       log.Errorf("could not reload backend config: %v", err)
+               } else {
+                       routing.SetBackendConfig(backendConfig)
+               }
        }
-       signalReloader(unix.SIGHUP, reloadProfilingConfig)
+       signalReloader(unix.SIGHUP, reloadProfilingAndBackendConfig)
 }
 
 func setupTrafficVault(riakConfigFileName string, cfg *config.Config) 
trafficvault.TrafficVault {
@@ -293,6 +312,19 @@ func setupTrafficVault(riakConfigFileName string, cfg 
*config.Config) trafficvau
        return &disabled.Disabled{}
 }
 
+func getNewBackendConfig(backendConfigFileName *string) (config.BackendConfig, 
error) {
+       if backendConfigFileName == nil {
+               return config.BackendConfig{}, errors.New("no backend config 
filename")
+       }
+       log.Infof("setting new backend config to %s", *backendConfigFileName)
+       backendConfig, err := config.LoadBackendConfig(*backendConfigFileName)
+       if err != nil {
+               log.Errorf("error reloading config: %v", err)
+               return backendConfig, err
+       }
+       return backendConfig, nil
+}
+
 func setNewProfilingInfo(configFileName string, currentProfilingEnabled *bool, 
currentProfilingLocation *string, version string) {
        newProfilingEnabled, newProfilingLocation, err := 
reloadProfilingInfo(configFileName)
        if err != nil {

Reply via email to