conf: support render configure file using environment variables

This commit is contained in:
fatedier 2018-12-11 11:46:12 +08:00
parent b61cb14c8f
commit 25cfda5768
6 changed files with 167 additions and 48 deletions

View File

@ -17,13 +17,10 @@ package client
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
ini "github.com/vaughan0/go-ini"
"github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/g" "github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "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]") log.Info("Http request: [/api/reload]")
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile) content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile)
if err != nil { if err != nil {
res.Code = 1 res.Code = 1
res.Msg = err.Error() res.Msg = err.Error()
log.Error("reload frpc config file error: %v", err) log.Error("reload frpc config file error: %v", err)
return return
} }
content := string(b)
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content) newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
if err != nil { if err != nil {
@ -70,15 +66,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, newCommonCfg.Start)
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)
if err != nil { if err != nil {
res.Code = 3 res.Code = 3
res.Msg = err.Error() res.Msg = err.Error()

View File

@ -17,7 +17,6 @@ package sub
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os" "os"
"os/signal" "os/signal"
@ -27,7 +26,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
ini "github.com/vaughan0/go-ini"
"github.com/fatedier/frp/client" "github.com/fatedier/frp/client"
"github.com/fatedier/frp/g" "github.com/fatedier/frp/g"
@ -111,9 +109,9 @@ func handleSignal(svr *client.Service) {
os.Exit(0) os.Exit(0)
} }
func parseClientCommonCfg(fileType int, filePath string) (err error) { func parseClientCommonCfg(fileType int, content string) (err error) {
if fileType == CfgFileTypeIni { if fileType == CfgFileTypeIni {
err = parseClientCommonCfgFromIni(filePath) err = parseClientCommonCfgFromIni(content)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
err = parseClientCommonCfgFromCmd() err = parseClientCommonCfgFromCmd()
} }
@ -121,8 +119,6 @@ func parseClientCommonCfg(fileType int, filePath string) (err error) {
return return
} }
g.GlbClientCfg.CfgFile = cfgFile
err = g.GlbClientCfg.ClientCommonConf.Check() err = g.GlbClientCfg.ClientCommonConf.Check()
if err != nil { if err != nil {
return return
@ -130,13 +126,7 @@ func parseClientCommonCfg(fileType int, filePath string) (err error) {
return return
} }
func parseClientCommonCfgFromIni(filePath string) (err error) { func parseClientCommonCfgFromIni(content string) (err error) {
b, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
content := string(b)
cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content) cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content)
if err != nil { if err != nil {
return err return err
@ -175,17 +165,19 @@ func parseClientCommonCfgFromCmd() (err error) {
} }
func runClient(cfgFilePath string) (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 { if err != nil {
return return
} }
conf, err := ini.LoadFile(cfgFilePath) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, g.GlbClientCfg.Start)
if err != nil {
return err
}
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start)
if err != nil { if err != nil {
return err return err
} }

View File

@ -16,7 +16,6 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -100,7 +99,13 @@ var rootCmd = &cobra.Command{
var err error var err error
if cfgFile != "" { 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 { } else {
err = parseServerCommonCfg(CfgFileTypeCmd, "") 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 { if fileType == CfgFileTypeIni {
err = parseServerCommonCfgFromIni(filePath) err = parseServerCommonCfgFromIni(content)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
err = parseServerCommonCfgFromCmd() err = parseServerCommonCfgFromCmd()
} }
@ -133,8 +138,6 @@ func parseServerCommonCfg(fileType int, filePath string) (err error) {
return return
} }
g.GlbServerCfg.CfgFile = filePath
err = g.GlbServerCfg.ServerCommonConf.Check() err = g.GlbServerCfg.ServerCommonConf.Check()
if err != nil { if err != nil {
return return
@ -144,13 +147,7 @@ func parseServerCommonCfg(fileType int, filePath string) (err error) {
return return
} }
func parseServerCommonCfgFromIni(filePath string) (err error) { func parseServerCommonCfgFromIni(content string) (err error) {
b, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
content := string(b)
cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content) cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content)
if err != nil { if err != nil {
return err return err

View File

@ -885,9 +885,15 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in
// if len(startProxy) is 0, start all // if len(startProxy) is 0, start all
// otherwise just start proxies in startProxy map // 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) { 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 != "" { if prefix != "" {
prefix += "." prefix += "."
} }

64
models/config/value.go Normal file
View File

@ -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
}

72
tests/ci/template_test.go Normal file
View File

@ -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)
}