Bläddra i källkod

conf: support render configure file using environment variables

fatedier 6 år sedan
förälder
incheckning
25cfda5768
6 ändrade filer med 164 tillägg och 45 borttagningar
  1. 2 14
      client/admin_api.go
  2. 9 17
      cmd/frpc/sub/root.go
  3. 10 13
      cmd/frps/root.go
  4. 7 1
      models/config/proxy.go
  5. 64 0
      models/config/value.go
  6. 72 0
      tests/ci/template_test.go

+ 2 - 14
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()

+ 9 - 17
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
 
-	conf, err := ini.LoadFile(cfgFilePath)
+	err = parseClientCommonCfg(CfgFileTypeIni, content)
 	if err != nil {
-		return err
+		return
 	}
 
-	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
 	}

+ 10 - 13
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

+ 7 - 1
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 += "."
 	}

+ 64 - 0
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
+}

+ 72 - 0
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)
+}