From 95681518606083c638840a31835c36d9efa289d2 Mon Sep 17 00:00:00 2001 From: Egor Zavialov Date: Sat, 1 Jun 2024 16:28:15 +0900 Subject: [PATCH] feat: add client reload on config change --- client/service.go | 68 +++++++++++++++++++++++++++++++++++++ conf/frpc_full_example.toml | 3 ++ go.mod | 1 + go.sum | 2 ++ pkg/config/v1/client.go | 3 ++ 5 files changed, 77 insertions(+) diff --git a/client/service.go b/client/service.go index 0cbd8757..722b3fef 100644 --- a/client/service.go +++ b/client/service.go @@ -30,6 +30,9 @@ import ( "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/pkg/auth" v1 "github.com/fatedier/frp/pkg/config/v1" + "github.com/fatedier/frp/pkg/config/v1/validation" + "github.com/radovskyb/watcher" + "github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/msg" httppkg "github.com/fatedier/frp/pkg/util/http" "github.com/fatedier/frp/pkg/util/log" @@ -156,6 +159,11 @@ func NewService(options ServiceOptions) (*Service, error) { if webServer != nil { webServer.RouteRegister(s.registerRouteHandlers) } + + if options.Common.ConfigAutoReload { + go s.MonitorClientConfig(options.ConfigFilePath) + } + return s, nil } @@ -408,3 +416,63 @@ type statusExporterImpl struct { func (s *statusExporterImpl) GetProxyStatus(name string) (*proxy.WorkingStatus, bool) { return s.getProxyStatusFunc(name) } + + +func (s *Service) MonitorClientConfig(cfgFilePath string) { + cliCfg, _, _, _, err := config.LoadClientConfig(cfgFilePath, false) + if err != nil { + log.Errorf("%v", err) + } + + if !cliCfg.ConfigAutoReload { + log.Infof("auto reload on config change is disabled.") + return + } + + w := watcher.New() + w.SetMaxEvents(1) + w.FilterOps(watcher.Write) + + done := make(chan bool) + + go func() { + for { + select { + case event := <-w.Event: + log.Infof("config file changed: %s", event.Path) + cliCfg, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(cfgFilePath, false) + if err != nil { + log.Infof("reload frpc proxy config error: %s", err.Error()) + continue + } + if _, err := validation.ValidateAllClientConfig(cliCfg, pxyCfgs, visitorCfgs); err != nil { + log.Infof("reload frpc proxy config error: %s", err.Error()) + continue + } + + if err := s.UpdateAllConfigurer(pxyCfgs, visitorCfgs); err != nil { + log.Infof("reload frpc proxy config error: %s", err.Error()) + continue + } + log.Infof("success reload conf") + case err := <-w.Error: + log.Infof("error: %s", err.Error()) + case <-w.Closed: + done <- true + return + } + } + }() + + if err := w.Add(cfgFilePath); err != nil { + log.Infof("error adding file to watcher: %s", err.Error()) + } + + go func() { + if err := w.Start(time.Second); err != nil { + log.Infof("error starting watcher: %s", err.Error()) + } + }() + + <-done +} \ No newline at end of file diff --git a/conf/frpc_full_example.toml b/conf/frpc_full_example.toml index c88087a1..d152ff97 100644 --- a/conf/frpc_full_example.toml +++ b/conf/frpc_full_example.toml @@ -12,6 +12,9 @@ serverPort = 7000 # STUN server to help penetrate NAT hole. # natHoleStunServer = "stun.easyvoip.com:3478" +# if true, autoreload is enabled +configAutoReload = true + # Decide if exit program when first login failed, otherwise continuous relogin to frps # default is true loginFailExit = true diff --git a/go.mod b/go.mod index 72bbc0ac..fc5f38b9 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect + github.com/radovskyb/watcher v1.0.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect diff --git a/go.sum b/go.sum index efcb18ee..7b07a83c 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= +github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rodaine/table v1.2.0 h1:38HEnwK4mKSHQJIkavVj+bst1TEY7j9zhLMWu4QJrMA= diff --git a/pkg/config/v1/client.go b/pkg/config/v1/client.go index d43ec1bc..4ba772c4 100644 --- a/pkg/config/v1/client.go +++ b/pkg/config/v1/client.go @@ -46,6 +46,9 @@ type ClientCommonConfig struct { ServerPort int `json:"serverPort,omitempty"` // STUN server to help penetrate NAT hole. NatHoleSTUNServer string `json:"natHoleStunServer,omitempty"` + + // + ConfigAutoReload bool `json:"configAutoReload,omitempty"` // DNSServer specifies a DNS server address for FRPC to use. If this value // is "", the default DNS will be used. DNSServer string `json:"dnsServer,omitempty"`