From a0799e73c36a872f35a4e6c6d421118ea06985ca Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Thu, 8 Nov 2018 05:31:09 +0800 Subject: [PATCH] PoC for getting router to reload gateway config. --- bridge/api/api.go | 21 +++++++++++++++------ bridge/config/config.go | 2 ++ gateway/router.go | 32 ++++++++++++++++++++++++++++++++ matterbridge.toml.sample | 7 +++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/bridge/api/api.go b/bridge/api/api.go index 478f3320a2..e8b43d3df0 100644 --- a/bridge/api/api.go +++ b/bridge/api/api.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -13,9 +14,11 @@ import ( "github.com/labstack/echo" "github.com/labstack/echo/middleware" "github.com/zfjagann/golang-ring" + "github.com/spf13/viper" ) type Api struct { + Server *echo.Echo Messages ring.Ring sync.RWMutex *bridge.Config @@ -30,8 +33,8 @@ type ApiMessage struct { } func New(cfg *bridge.Config) bridge.Bridger { - b := &Api{Config: cfg} e := echo.New() + b := &Api{Config: cfg, Server: e} e.HideBanner = true e.HidePort = true b.Messages = ring.Ring{} @@ -49,20 +52,24 @@ func New(cfg *bridge.Config) bridge.Bridger { e.GET("/api/messages", b.handleMessages) e.GET("/api/stream", b.handleStream) e.POST("/api/message", b.handlePostMessage) + return b +} + +func (b *Api) Connect() error { go func() { if b.GetString("BindAddress") == "" { b.Log.Fatalf("No BindAddress configured.") } b.Log.Infof("Listening on %s", b.GetString("BindAddress")) - b.Log.Fatal(e.Start(b.GetString("BindAddress"))) + b.Log.Info(b.Server.Start(b.GetString("BindAddress"))) }() - return b -} - -func (b *Api) Connect() error { return nil } func (b *Api) Disconnect() error { + ctx := context.Background() + if err := b.Server.Shutdown(ctx); err != nil { + b.Log.Info(err) + } return nil } @@ -116,7 +123,9 @@ func (b *Api) handleConfigReload(c echo.Context) error { b.Log.Error("Failed to write remote config file: ", err) return c.String(http.StatusInternalServerError, "Internal Server Error") } + viper.ReadInConfig() + b.Remote <- config.Message{Username: "system", Text: config.EVENT_RELOAD_CONFIG, Channel: "api", Account: "", Event: config.EVENT_RELOAD_CONFIG} return c.String(http.StatusAccepted, "Accepted") } diff --git a/bridge/config/config.go b/bridge/config/config.go index 40cc4f5f2d..04d456ee9d 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -23,6 +23,7 @@ const ( EVENT_USER_ACTION = "user_action" EVENT_MSG_DELETE = "msg_delete" EVENT_API_CONNECTED = "api_connected" + EVENT_RELOAD_CONFIG = "reload_config" ) type Message struct { @@ -211,6 +212,7 @@ func NewConfig(cfgfile string) *Config { } viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { + viper.Unmarshal(&cfg) flog.Println("Config file changed:", e.Name) }) diff --git a/gateway/router.go b/gateway/router.go index 3a45de369e..f07eab9708 100644 --- a/gateway/router.go +++ b/gateway/router.go @@ -59,6 +59,35 @@ func (r *Router) Start() error { return nil } +func (r *Router) Stop() error { + for _, gw := range r.Gateways { + for _, br := range gw.Bridges { + err := br.Disconnect() + if err != nil { + return err + } + } + } + return nil +} + +func (r *Router) Restart() error { + if err := r.Stop(); err != nil { + return err + } + + r, err = NewRouter(r.Config) + if err != nil { + return err + } + + if err = r.Start(); err != nil { + return err + } + + return nil +} + func (r *Router) getBridge(account string) *bridge.Bridge { for _, gw := range r.Gateways { if br, ok := gw.Bridges[account]; ok { @@ -70,6 +99,9 @@ func (r *Router) getBridge(account string) *bridge.Bridge { func (r *Router) handleReceive() { for msg := range r.Message { + if msg.Event == config.EVENT_RELOAD_CONFIG { + go r.Restart() + } if msg.Event == config.EVENT_FAILURE { Loop: for _, gw := range r.Gateways { diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 441a333cc7..3094b9d720 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -1228,6 +1228,13 @@ Buffer=1000 #OPTIONAL (no authorization if token is empty) Token="mytoken" +#URL from which to fetch and reload configuration with API call: +# PUT /api/reload +#Note: Affects global configuration, not just bridge or gateway! +#Highly recommended to use token auth if using this. +#OPTIONAL (default empty) +ConfigURL="https://example.com/matterbridge.toml" + #extra label that can be used in the RemoteNickFormat #optional (default empty) Label=""