From 3585f5c0c08a23cc840df1c16e4deb8c4ad719b9 Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 15 Mar 2024 17:23:16 +0800 Subject: [PATCH] support range ports mapping by go template (#4073) --- pkg/config/load.go | 5 +++- pkg/config/template.go | 52 +++++++++++++++++++++++++++++++++ test/e2e/framework/client.go | 12 ++++---- test/e2e/framework/expect.go | 4 +++ test/e2e/framework/framework.go | 2 +- test/e2e/legacy/basic/client.go | 5 ++-- test/e2e/legacy/basic/server.go | 3 +- test/e2e/v1/basic/client.go | 5 ++-- test/e2e/v1/basic/config.go | 45 ++++++++++++++++++++++++++++ test/e2e/v1/basic/server.go | 3 +- 10 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 pkg/config/template.go diff --git a/pkg/config/load.go b/pkg/config/load.go index cdbb8e9..f9a705e 100644 --- a/pkg/config/load.go +++ b/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 } diff --git a/pkg/config/template.go b/pkg/config/template.go new file mode 100644 index 0000000..44bc456 --- /dev/null +++ b/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) +} diff --git a/test/e2e/framework/client.go b/test/e2e/framework/client.go index 7661435..c13cee1 100644 --- a/test/e2e/framework/client.go +++ b/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) } diff --git a/test/e2e/framework/expect.go b/test/e2e/framework/expect.go index dab6da7..3c357bb 100644 --- a/test/e2e/framework/expect.go +++ b/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...) diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index e0be5af..527096c 100644 --- a/test/e2e/framework/framework.go +++ b/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 } diff --git a/test/e2e/legacy/basic/client.go b/test/e2e/legacy/basic/client.go index d4862e5..98f675b 100644 --- a/test/e2e/legacy/basic/client.go +++ b/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) diff --git a/test/e2e/legacy/basic/server.go b/test/e2e/legacy/basic/server.go index ca6717c..62bfd62 100644 --- a/test/e2e/legacy/basic/server.go +++ b/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") diff --git a/test/e2e/v1/basic/client.go b/test/e2e/v1/basic/client.go index b0b258d..ef5e262 100644 --- a/test/e2e/v1/basic/client.go +++ b/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) diff --git a/test/e2e/v1/basic/config.go b/test/e2e/v1/basic/config.go index 686b3c5..7dc3ced 100644 --- a/test/e2e/v1/basic/config.go +++ b/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() { diff --git a/test/e2e/v1/basic/server.go b/test/e2e/v1/basic/server.go index f2f4e0a..fe8af4d 100644 --- a/test/e2e/v1/basic/server.go +++ b/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")