From 25cfda5768a1b4b34a3b6494e02f662056ab289a Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 11 Dec 2018 11:46:12 +0800 Subject: [PATCH] conf: support render configure file using environment variables --- client/admin_api.go | 16 ++------- cmd/frpc/sub/root.go | 32 +++++++---------- cmd/frps/root.go | 23 ++++++------- models/config/proxy.go | 8 ++++- models/config/value.go | 64 ++++++++++++++++++++++++++++++++++ tests/ci/template_test.go | 72 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 models/config/value.go create mode 100644 tests/ci/template_test.go diff --git a/client/admin_api.go b/client/admin_api.go index 893d127..76f3a0b 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -17,13 +17,10 @@ package client import ( "encoding/json" "fmt" - "io/ioutil" "net/http" "sort" "strings" - ini "github.com/vaughan0/go-ini" - "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config" @@ -53,14 +50,13 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { log.Info("Http request: [/api/reload]") - b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile) + content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile) if err != nil { res.Code = 1 res.Msg = err.Error() log.Error("reload frpc config file error: %v", err) return } - content := string(b) newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content) if err != nil { @@ -70,15 +66,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { return } - conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile) - if err != nil { - res.Code = 1 - res.Msg = err.Error() - log.Error("reload frpc config file error: %v", err) - return - } - - pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, newCommonCfg.Start) if err != nil { res.Code = 3 res.Msg = err.Error() diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index ca3b853..c770b7a 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -17,7 +17,6 @@ package sub import ( "context" "fmt" - "io/ioutil" "net" "os" "os/signal" @@ -27,7 +26,6 @@ import ( "time" "github.com/spf13/cobra" - ini "github.com/vaughan0/go-ini" "github.com/fatedier/frp/client" "github.com/fatedier/frp/g" @@ -111,9 +109,9 @@ func handleSignal(svr *client.Service) { os.Exit(0) } -func parseClientCommonCfg(fileType int, filePath string) (err error) { +func parseClientCommonCfg(fileType int, content string) (err error) { if fileType == CfgFileTypeIni { - err = parseClientCommonCfgFromIni(filePath) + err = parseClientCommonCfgFromIni(content) } else if fileType == CfgFileTypeCmd { err = parseClientCommonCfgFromCmd() } @@ -121,8 +119,6 @@ func parseClientCommonCfg(fileType int, filePath string) (err error) { return } - g.GlbClientCfg.CfgFile = cfgFile - err = g.GlbClientCfg.ClientCommonConf.Check() if err != nil { return @@ -130,13 +126,7 @@ func parseClientCommonCfg(fileType int, filePath string) (err error) { return } -func parseClientCommonCfgFromIni(filePath string) (err error) { - b, err := ioutil.ReadFile(filePath) - if err != nil { - return err - } - content := string(b) - +func parseClientCommonCfgFromIni(content string) (err error) { cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content) if err != nil { return err @@ -175,17 +165,19 @@ func parseClientCommonCfgFromCmd() (err error) { } func runClient(cfgFilePath string) (err error) { - err = parseClientCommonCfg(CfgFileTypeIni, cfgFilePath) + var content string + content, err = config.GetRenderedConfFromFile(cfgFilePath) + if err != nil { + return + } + g.GlbClientCfg.CfgFile = cfgFilePath + + err = parseClientCommonCfg(CfgFileTypeIni, content) if err != nil { return } - conf, err := ini.LoadFile(cfgFilePath) - if err != nil { - return err - } - - pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start) + pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, g.GlbClientCfg.Start) if err != nil { return err } diff --git a/cmd/frps/root.go b/cmd/frps/root.go index b4ccff4..11368b2 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -16,7 +16,6 @@ package main import ( "fmt" - "io/ioutil" "os" "github.com/spf13/cobra" @@ -100,7 +99,13 @@ var rootCmd = &cobra.Command{ var err error if cfgFile != "" { - err = parseServerCommonCfg(CfgFileTypeIni, cfgFile) + var content string + content, err = config.GetRenderedConfFromFile(cfgFile) + if err != nil { + return err + } + g.GlbServerCfg.CfgFile = cfgFile + err = parseServerCommonCfg(CfgFileTypeIni, content) } else { err = parseServerCommonCfg(CfgFileTypeCmd, "") } @@ -123,9 +128,9 @@ func Execute() { } } -func parseServerCommonCfg(fileType int, filePath string) (err error) { +func parseServerCommonCfg(fileType int, content string) (err error) { if fileType == CfgFileTypeIni { - err = parseServerCommonCfgFromIni(filePath) + err = parseServerCommonCfgFromIni(content) } else if fileType == CfgFileTypeCmd { err = parseServerCommonCfgFromCmd() } @@ -133,8 +138,6 @@ func parseServerCommonCfg(fileType int, filePath string) (err error) { return } - g.GlbServerCfg.CfgFile = filePath - err = g.GlbServerCfg.ServerCommonConf.Check() if err != nil { return @@ -144,13 +147,7 @@ func parseServerCommonCfg(fileType int, filePath string) (err error) { return } -func parseServerCommonCfgFromIni(filePath string) (err error) { - b, err := ioutil.ReadFile(filePath) - if err != nil { - return err - } - content := string(b) - +func parseServerCommonCfgFromIni(content string) (err error) { cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content) if err != nil { return err diff --git a/models/config/proxy.go b/models/config/proxy.go index 0766b5a..cdb06b8 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -885,9 +885,15 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in // if len(startProxy) is 0, start all // otherwise just start proxies in startProxy map -func LoadAllConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) ( +func LoadAllConfFromIni(prefix string, content string, startProxy map[string]struct{}) ( proxyConfs map[string]ProxyConf, visitorConfs map[string]VisitorConf, err error) { + conf, errRet := ini.Load(strings.NewReader(content)) + if errRet != nil { + err = errRet + return + } + if prefix != "" { prefix += "." } diff --git a/models/config/value.go b/models/config/value.go new file mode 100644 index 0000000..3457024 --- /dev/null +++ b/models/config/value.go @@ -0,0 +1,64 @@ +package config + +import ( + "bytes" + "io/ioutil" + "os" + "strings" + "text/template" +) + +var ( + glbEnvs map[string]string +) + +func init() { + glbEnvs = make(map[string]string) + envs := os.Environ() + for _, env := range envs { + kv := strings.Split(env, "=") + if len(kv) != 2 { + continue + } + glbEnvs[kv[0]] = kv[1] + } +} + +type Values struct { + Envs map[string]string // environment vars +} + +func GetValues() *Values { + return &Values{ + Envs: glbEnvs, + } +} + +func RenderContent(in string) (out string, err error) { + tmpl, errRet := template.New("frp").Parse(in) + if errRet != nil { + err = errRet + return + } + + buffer := bytes.NewBufferString("") + v := GetValues() + err = tmpl.Execute(buffer, v) + if err != nil { + return + } + out = buffer.String() + return +} + +func GetRenderedConfFromFile(path string) (out string, err error) { + var b []byte + b, err = ioutil.ReadFile(path) + if err != nil { + return + } + content := string(b) + + out, err = RenderContent(content) + return +} diff --git a/tests/ci/template_test.go b/tests/ci/template_test.go new file mode 100644 index 0000000..09b8417 --- /dev/null +++ b/tests/ci/template_test.go @@ -0,0 +1,72 @@ +package ci + +import ( + "os" + "testing" + "time" + + "github.com/fatedier/frp/tests/config" + "github.com/fatedier/frp/tests/consts" + "github.com/fatedier/frp/tests/util" + + "github.com/stretchr/testify/assert" +) + +const FRPS_TEMPLATE_CONF = ` +[common] +bind_addr = 0.0.0.0 +bind_port = {{ .Envs.SERVER_PORT }} +log_file = console +# debug, info, warn, error +log_level = debug +token = 123456 +` + +const FRPC_TEMPLATE_CONF = ` +[common] +server_addr = 127.0.0.1 +server_port = 20000 +log_file = console +# debug, info, warn, error +log_level = debug +token = {{ .Envs.FRP_TOKEN }} + +[tcp] +type = tcp +local_port = 10701 +remote_port = {{ .Envs.TCP_REMOTE_PORT }} +` + +func TestConfTemplate(t *testing.T) { + assert := assert.New(t) + frpsCfgPath, err := config.GenerateConfigFile(consts.FRPS_NORMAL_CONFIG, FRPS_TEMPLATE_CONF) + if assert.NoError(err) { + defer os.Remove(frpsCfgPath) + } + + frpcCfgPath, err := config.GenerateConfigFile(consts.FRPC_NORMAL_CONFIG, FRPC_TEMPLATE_CONF) + if assert.NoError(err) { + defer os.Remove(frpcCfgPath) + } + + frpsProcess := util.NewProcess("env", []string{"SERVER_PORT=20000", consts.FRPS_BIN_PATH, "-c", frpsCfgPath}) + err = frpsProcess.Start() + if assert.NoError(err) { + defer frpsProcess.Stop() + } + + time.Sleep(100 * time.Millisecond) + + frpcProcess := util.NewProcess("env", []string{"FRP_TOKEN=123456", "TCP_REMOTE_PORT=20801", consts.FRPC_BIN_PATH, "-c", frpcCfgPath}) + err = frpcProcess.Start() + if assert.NoError(err) { + defer frpcProcess.Stop() + } + + time.Sleep(250 * time.Millisecond) + + // test tcp1 + res, err := util.SendTcpMsg("127.0.0.1:20801", consts.TEST_TCP_ECHO_STR) + assert.NoError(err) + assert.Equal(consts.TEST_TCP_ECHO_STR, res) +}