Compare commits

...

4 Commits

Author SHA1 Message Date
fatedier
231c1dc6bd
Merge ced2fb69b799410c4147677dc82633041b5008fb into acf33db4e4b6c9cf9182d93280299010637b6324 2024-03-15 19:48:12 +08:00
fatedier
acf33db4e4
update release notes (#4074) 2024-03-15 17:50:58 +08:00
fatedier
3585f5c0c0
support range ports mapping by go template (#4073) 2024-03-15 17:23:16 +08:00
fatedier
ced2fb69b7 use math/rand/v2 2024-02-22 21:09:29 +08:00
15 changed files with 142 additions and 26 deletions

View File

@ -1,3 +1,21 @@
### Features
* Support range ports mapping in TOML/YAML/JSON configuration file by using go template syntax.
For example:
```
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
[[proxies]]
name = "tcp-{{ $v.First }}"
type = "tcp"
localPort = {{ $v.First }}
remotePort = {{ $v.Second }}
{{- end }}
```
This will create 8 proxies such as `tcp-6000, tcp-6001, ... tcp-6007`.
### Fixes ### Fixes
* Fix the issue of incorrect interval time for rotating the log by day. * Fix the issue of incorrect interval time for rotating the log by day.

View File

@ -80,7 +80,10 @@ func DetectLegacyINIFormatFromFile(path string) bool {
} }
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) { 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 { if err != nil {
return nil, err return nil, err
} }

52
pkg/config/template.go Normal file
View File

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

View File

@ -17,7 +17,7 @@ package nathole
import ( import (
"context" "context"
"fmt" "fmt"
"math/rand" "math/rand/v2"
"net" "net"
"slices" "slices"
"strconv" "strconv"
@ -341,7 +341,7 @@ func sendSidMessage(
TransactionID: transactionID, TransactionID: transactionID,
Sid: sid, Sid: sid,
Response: false, Response: false,
Nonce: strings.Repeat("0", rand.Intn(20)), Nonce: strings.Repeat("0", rand.IntN(20)),
} }
buf, err := EncodeMessage(m, key) buf, err := EncodeMessage(m, key)
if err != nil { if err != nil {
@ -398,7 +398,7 @@ func sendSidMessageToRandomPorts(
used := sets.New[int]() used := sets.New[int]()
getUnusedPort := func() int { getUnusedPort := func() int {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
port := rand.Intn(65535-1024) + 1024 port := rand.IntN(65535-1024) + 1024
if !used.Has(port) { if !used.Has(port) {
used.Insert(port) used.Insert(port)
return port return port

View File

@ -20,7 +20,7 @@ import (
"crypto/subtle" "crypto/subtle"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
mathrand "math/rand" mathrand "math/rand/v2"
"net" "net"
"strconv" "strconv"
"strings" "strings"
@ -124,7 +124,7 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
if max <= min { if max <= min {
n = min n = min
} else { } else {
n = mathrand.Int63n(max-min) + min n = mathrand.Int64N(max-min) + min
} }
d := duration * time.Duration(n) / time.Duration(1000) d := duration * time.Duration(n) / time.Duration(1000)
time.Sleep(d) time.Sleep(d)

View File

@ -14,7 +14,7 @@
package version package version
var version = "0.55.1" var version = "0.56.0"
func Full() string { func Full() string {
return version return version

View File

@ -15,7 +15,7 @@
package wait package wait
import ( import (
"math/rand" "math/rand/v2"
"time" "time"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"

View File

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

View File

@ -43,6 +43,10 @@ func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...) 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. // ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) { func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...) gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)

View File

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

View File

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

View File

@ -7,7 +7,6 @@ import (
"github.com/onsi/ginkgo/v2" "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"
"github.com/fatedier/frp/test/e2e/framework/consts" "github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port" "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}) f.RunProcesses([]string{serverConf}, []string{clientConf})
client := clientsdk.New("127.0.0.1", adminPort) client := f.APIClientForFrpc(adminPort)
// tcp random port // tcp random port
status, err := client.GetProxyStatus("tcp") status, err := client.GetProxyStatus("tcp")

View File

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

View File

@ -38,6 +38,51 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
framework.NewRequestExpect(f).PortName(portName).Ensure() 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() { ginkgo.Describe("Includes", func() {

View File

@ -7,7 +7,6 @@ import (
"github.com/onsi/ginkgo/v2" "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"
"github.com/fatedier/frp/test/e2e/framework/consts" "github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port" "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}) f.RunProcesses([]string{serverConf}, []string{clientConf})
client := clientsdk.New("127.0.0.1", adminPort) client := f.APIClientForFrpc(adminPort)
// tcp random port // tcp random port
status, err := client.GetProxyStatus("tcp") status, err := client.GetProxyStatus("tcp")