Selaa lähdekoodia

support range ports mapping by go template (#4073)

fatedier 1 vuosi sitten
vanhempi
commit
3585f5c0c0

+ 4 - 1
pkg/config/load.go

@@ -80,7 +80,10 @@ func DetectLegacyINIFormatFromFile(path string) bool {
 }
 
 func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
-	tmpl, err := template.New("frp").Parse(string(in))
+	tmpl, err := template.New("frp").Funcs(template.FuncMap{
+		"parseNumberRange":     parseNumberRange,
+		"parseNumberRangePair": parseNumberRangePair,
+	}).Parse(string(in))
 	if err != nil {
 		return nil, err
 	}

+ 52 - 0
pkg/config/template.go

@@ -0,0 +1,52 @@
+// Copyright 2024 The frp Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"fmt"
+
+	"github.com/fatedier/frp/pkg/util/util"
+)
+
+type NumberPair struct {
+	First  int64
+	Second int64
+}
+
+func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, error) {
+	firstRangeNumbers, err := util.ParseRangeNumbers(firstRangeStr)
+	if err != nil {
+		return nil, err
+	}
+	secondRangeNumbers, err := util.ParseRangeNumbers(secondRangeStr)
+	if err != nil {
+		return nil, err
+	}
+	if len(firstRangeNumbers) != len(secondRangeNumbers) {
+		return nil, fmt.Errorf("first and second range numbers are not in pairs")
+	}
+	pairs := make([]NumberPair, 0, len(firstRangeNumbers))
+	for i := 0; i < len(firstRangeNumbers); i++ {
+		pairs = append(pairs, NumberPair{
+			First:  firstRangeNumbers[i],
+			Second: secondRangeNumbers[i],
+		})
+	}
+	return pairs, nil
+}
+
+func parseNumberRange(firstRangeStr string) ([]int64, error) {
+	return util.ParseRangeNumbers(firstRangeStr)
+}

+ 5 - 7
test/e2e/framework/client.go

@@ -1,11 +1,9 @@
 package framework
 
-type FRPClient struct {
-	port int
-}
+import (
+	clientsdk "github.com/fatedier/frp/pkg/sdk/client"
+)
 
-func (f *Framework) FRPClient(port int) *FRPClient {
-	return &FRPClient{
-		port: port,
-	}
+func (f *Framework) APIClientForFrpc(port int) *clientsdk.Client {
+	return clientsdk.New("127.0.0.1", port)
 }

+ 4 - 0
test/e2e/framework/expect.go

@@ -43,6 +43,10 @@ func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
 	gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
 }
 
+func ExpectContainSubstring(actual, substr string, explain ...interface{}) {
+	gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...)
+}
+
 // ExpectConsistOf expects actual contains precisely the extra elements.  The ordering of the elements does not matter.
 func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
 	gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)

+ 1 - 1
test/e2e/framework/framework.go

@@ -217,7 +217,7 @@ func (f *Framework) RenderTemplates(templates []string) (outs []string, ports ma
 	}
 
 	for _, t := range templates {
-		tmpl, err := template.New("").Parse(t)
+		tmpl, err := template.New("frp-e2e").Parse(t)
 		if err != nil {
 			return nil, nil, err
 		}

+ 2 - 3
test/e2e/legacy/basic/client.go

@@ -8,7 +8,6 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 
-	clientsdk "github.com/fatedier/frp/pkg/sdk/client"
 	"github.com/fatedier/frp/test/e2e/framework"
 	"github.com/fatedier/frp/test/e2e/framework/consts"
 	"github.com/fatedier/frp/test/e2e/pkg/request"
@@ -54,7 +53,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
 		framework.NewRequestExpect(f).Port(p2Port).Ensure()
 		framework.NewRequestExpect(f).Port(p3Port).Ensure()
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 		conf, err := client.GetConfig()
 		framework.ExpectNoError(err)
 
@@ -120,7 +119,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
 
 		framework.NewRequestExpect(f).Port(testPort).Ensure()
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 		err := client.Stop()
 		framework.ExpectNoError(err)
 

+ 1 - 2
test/e2e/legacy/basic/server.go

@@ -7,7 +7,6 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 
-	clientsdk "github.com/fatedier/frp/pkg/sdk/client"
 	"github.com/fatedier/frp/test/e2e/framework"
 	"github.com/fatedier/frp/test/e2e/framework/consts"
 	"github.com/fatedier/frp/test/e2e/pkg/port"
@@ -99,7 +98,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
 
 		f.RunProcesses([]string{serverConf}, []string{clientConf})
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 
 		// tcp random port
 		status, err := client.GetProxyStatus("tcp")

+ 2 - 3
test/e2e/v1/basic/client.go

@@ -8,7 +8,6 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 
-	clientsdk "github.com/fatedier/frp/pkg/sdk/client"
 	"github.com/fatedier/frp/test/e2e/framework"
 	"github.com/fatedier/frp/test/e2e/framework/consts"
 	"github.com/fatedier/frp/test/e2e/pkg/request"
@@ -57,7 +56,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
 		framework.NewRequestExpect(f).Port(p2Port).Ensure()
 		framework.NewRequestExpect(f).Port(p3Port).Ensure()
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 		conf, err := client.GetConfig()
 		framework.ExpectNoError(err)
 
@@ -124,7 +123,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
 
 		framework.NewRequestExpect(f).Port(testPort).Ensure()
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 		err := client.Stop()
 		framework.ExpectNoError(err)
 

+ 45 - 0
test/e2e/v1/basic/config.go

@@ -38,6 +38,51 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
 
 			framework.NewRequestExpect(f).PortName(portName).Ensure()
 		})
+
+		ginkgo.It("Range ports mapping", func() {
+			serverConf := consts.DefaultServerConfig
+			clientConf := consts.DefaultClientConfig
+
+			adminPort := f.AllocPort()
+
+			localPortsRange := "13010-13012,13014"
+			remotePortsRange := "23010-23012,23014"
+			escapeTemplate := func(s string) string {
+				return "{{ `" + s + "` }}"
+			}
+			clientConf += fmt.Sprintf(`
+			webServer.port = %d
+
+			%s
+			[[proxies]]
+			name = "tcp-%s"
+			type = "tcp"
+			localPort = %s
+			remotePort = %s
+			%s
+			`, adminPort,
+				escapeTemplate(fmt.Sprintf(`{{- range $_, $v := parseNumberRangePair "%s" "%s" }}`, localPortsRange, remotePortsRange)),
+				escapeTemplate("{{ $v.First }}"),
+				escapeTemplate("{{ $v.First }}"),
+				escapeTemplate("{{ $v.Second }}"),
+				escapeTemplate("{{- end }}"),
+			)
+
+			f.RunProcesses([]string{serverConf}, []string{clientConf})
+
+			client := f.APIClientForFrpc(adminPort)
+			checkProxyFn := func(name string, localPort, remotePort int) {
+				status, err := client.GetProxyStatus(name)
+				framework.ExpectNoError(err)
+
+				framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort))
+				framework.ExpectContainSubstring(status.RemoteAddr, fmt.Sprintf(":%d", remotePort))
+			}
+			checkProxyFn("tcp-13010", 13010, 23010)
+			checkProxyFn("tcp-13011", 13011, 23011)
+			checkProxyFn("tcp-13012", 13012, 23012)
+			checkProxyFn("tcp-13014", 13014, 23014)
+		})
 	})
 
 	ginkgo.Describe("Includes", func() {

+ 1 - 2
test/e2e/v1/basic/server.go

@@ -7,7 +7,6 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 
-	clientsdk "github.com/fatedier/frp/pkg/sdk/client"
 	"github.com/fatedier/frp/test/e2e/framework"
 	"github.com/fatedier/frp/test/e2e/framework/consts"
 	"github.com/fatedier/frp/test/e2e/pkg/port"
@@ -110,7 +109,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
 
 		f.RunProcesses([]string{serverConf}, []string{clientConf})
 
-		client := clientsdk.New("127.0.0.1", adminPort)
+		client := f.APIClientForFrpc(adminPort)
 
 		// tcp random port
 		status, err := client.GetProxyStatus("tcp")