mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-17 23:14:21 +01:00
check release
This commit is contained in:
@@ -12,11 +12,12 @@ type Args struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CommandStart = ""
|
||||
CommandValidate = "validate"
|
||||
CommandListConfigs = "ls-config"
|
||||
CommandListRoutes = "ls-routes"
|
||||
CommandReload = "reload"
|
||||
CommandStart = ""
|
||||
CommandValidate = "validate"
|
||||
CommandListConfigs = "ls-config"
|
||||
CommandListRoutes = "ls-routes"
|
||||
CommandReload = "reload"
|
||||
CommandDebugListEntries = "debug-ls-entries"
|
||||
)
|
||||
|
||||
var ValidCommands = []string{
|
||||
@@ -25,6 +26,7 @@ var ValidCommands = []string{
|
||||
CommandListConfigs,
|
||||
CommandListRoutes,
|
||||
CommandReload,
|
||||
CommandDebugListEntries,
|
||||
}
|
||||
|
||||
func GetArgs() Args {
|
||||
|
||||
@@ -53,14 +53,15 @@ var WellKnownHTTPPorts = map[uint16]bool{
|
||||
|
||||
var (
|
||||
ServiceNamePortMapTCP = map[string]int{
|
||||
"postgres": 5432,
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"redis": 6379,
|
||||
"mssql": 1433,
|
||||
"memcached": 11211,
|
||||
"rabbitmq": 5672,
|
||||
"mongo": 27017,
|
||||
"postgres": 5432,
|
||||
"mysql": 3306,
|
||||
"mariadb": 3306,
|
||||
"redis": 6379,
|
||||
"mssql": 1433,
|
||||
"memcached": 11211,
|
||||
"rabbitmq": 5672,
|
||||
"mongo": 27017,
|
||||
"minecraft-server": 25565,
|
||||
|
||||
"dns": 53,
|
||||
"ssh": 22,
|
||||
|
||||
@@ -52,11 +52,6 @@ func (cfg *Config) GetAutoCertProvider() *autocert.Provider {
|
||||
return cfg.autocertProvider
|
||||
}
|
||||
|
||||
func (cfg *Config) StartProxyProviders() {
|
||||
cfg.startProviders()
|
||||
cfg.watchChanges()
|
||||
}
|
||||
|
||||
func (cfg *Config) Dispose() {
|
||||
if cfg.watcherCancel != nil {
|
||||
cfg.watcherCancel()
|
||||
@@ -70,10 +65,48 @@ func (cfg *Config) Reload() E.NestedError {
|
||||
if err := cfg.load(); err.HasError() {
|
||||
return err
|
||||
}
|
||||
cfg.startProviders()
|
||||
cfg.StartProxyProviders()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) StartProxyProviders() {
|
||||
cfg.controlProviders("start", (*PR.Provider).StartAllRoutes)
|
||||
}
|
||||
|
||||
func (cfg *Config) WatchChanges() {
|
||||
cfg.watcherCtx, cfg.watcherCancel = context.WithCancel(context.Background())
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-cfg.watcherCtx.Done():
|
||||
return
|
||||
case <-cfg.reloadReq:
|
||||
if err := cfg.Reload(); err.HasError() {
|
||||
cfg.l.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
eventCh, errCh := cfg.watcher.Events(cfg.watcherCtx)
|
||||
for {
|
||||
select {
|
||||
case <-cfg.watcherCtx.Done():
|
||||
return
|
||||
case event := <-eventCh:
|
||||
if event.Action.IsDelete() {
|
||||
cfg.stopProviders()
|
||||
} else {
|
||||
cfg.reloadReq <- struct{}{}
|
||||
}
|
||||
case err := <-errCh:
|
||||
cfg.l.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (cfg *Config) FindRoute(alias string) R.Route {
|
||||
return F.MapFind(cfg.proxyProviders,
|
||||
func(p *PR.Provider) (R.Route, bool) {
|
||||
@@ -131,6 +164,14 @@ func (cfg *Config) Statistics() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) DumpEntries() map[string]*M.ProxyEntry {
|
||||
entries := make(map[string]*M.ProxyEntry)
|
||||
cfg.forEachRoute(func(alias string, r R.Route, p *PR.Provider) {
|
||||
entries[alias] = r.Entry()
|
||||
})
|
||||
return entries
|
||||
}
|
||||
|
||||
func (cfg *Config) forEachRoute(do func(alias string, r R.Route, p *PR.Provider)) {
|
||||
cfg.proxyProviders.RangeAll(func(_ string, p *PR.Provider) {
|
||||
p.RangeRoutes(func(a string, r R.Route) {
|
||||
@@ -139,40 +180,6 @@ func (cfg *Config) forEachRoute(do func(alias string, r R.Route, p *PR.Provider)
|
||||
})
|
||||
}
|
||||
|
||||
func (cfg *Config) watchChanges() {
|
||||
cfg.watcherCtx, cfg.watcherCancel = context.WithCancel(context.Background())
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-cfg.watcherCtx.Done():
|
||||
return
|
||||
case <-cfg.reloadReq:
|
||||
if err := cfg.Reload(); err.HasError() {
|
||||
cfg.l.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
eventCh, errCh := cfg.watcher.Events(cfg.watcherCtx)
|
||||
for {
|
||||
select {
|
||||
case <-cfg.watcherCtx.Done():
|
||||
return
|
||||
case event := <-eventCh:
|
||||
if event.Action.IsDelete() {
|
||||
cfg.stopProviders()
|
||||
} else {
|
||||
cfg.reloadReq <- struct{}{}
|
||||
}
|
||||
case err := <-errCh:
|
||||
cfg.l.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (cfg *Config) load() (res E.NestedError) {
|
||||
b := E.NewBuilder("errors loading config")
|
||||
defer b.To(&res)
|
||||
@@ -257,10 +264,6 @@ func (cfg *Config) controlProviders(action string, do func(*PR.Provider) E.Neste
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) startProviders() {
|
||||
cfg.controlProviders("start", (*PR.Provider).StartAllRoutes)
|
||||
}
|
||||
|
||||
func (cfg *Config) stopProviders() {
|
||||
cfg.controlProviders("stop routes", (*PR.Provider).StopAllRoutes)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func FromDocker(c *types.Container, dockerHost string) (res Container) {
|
||||
StopMethod: res.getDeleteLabel(LabelStopMethod),
|
||||
StopTimeout: res.getDeleteLabel(LabelStopTimeout),
|
||||
StopSignal: res.getDeleteLabel(LabelStopSignal),
|
||||
Running: c.Status == "running",
|
||||
Running: c.Status == "running" || c.State == "running",
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func (c Container) getName() string {
|
||||
|
||||
func (c Container) getImageName() string {
|
||||
colonSep := strings.Split(c.Image, ":")
|
||||
slashSep := strings.Split(colonSep[len(colonSep)-1], "/")
|
||||
slashSep := strings.Split(colonSep[0], "/")
|
||||
return slashSep[len(slashSep)-1]
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ type Label struct {
|
||||
// Returns:
|
||||
// - error: an error if the field does not exist.
|
||||
func ApplyLabel[T any](obj *T, l *Label) E.NestedError {
|
||||
return U.SetFieldFromSnake(obj, l.Attribute, l.Value)
|
||||
return U.Deserialize(map[string]any{l.Attribute: l.Value}, obj)
|
||||
}
|
||||
|
||||
type ValueParser func(string) (any, E.NestedError)
|
||||
|
||||
@@ -36,17 +36,17 @@ func TestBuilderNested(t *testing.T) {
|
||||
expected1 :=
|
||||
(`error occurred:
|
||||
- Action 1 failed:
|
||||
- invalid Inner - 1
|
||||
- invalid Inner - 2
|
||||
- invalid Inner: 1
|
||||
- invalid Inner: 2
|
||||
- Action 2 failed:
|
||||
- invalid Inner - 3`)
|
||||
- invalid Inner: 3`)
|
||||
expected2 :=
|
||||
(`error occurred:
|
||||
- Action 1 failed:
|
||||
- invalid Inner - 2
|
||||
- invalid Inner - 1
|
||||
- invalid Inner: 2
|
||||
- invalid Inner: 1
|
||||
- Action 2 failed:
|
||||
- invalid Inner - 3`)
|
||||
- invalid Inner: 3`)
|
||||
if got != expected1 && got != expected2 {
|
||||
t.Errorf("expected \n%s, got \n%s", expected1, got)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,10 @@ type (
|
||||
NestedError = *nestedError
|
||||
nestedError struct {
|
||||
subject string
|
||||
err error // can be nil
|
||||
err error
|
||||
extras []nestedError
|
||||
severity Severity
|
||||
}
|
||||
errorInterface struct {
|
||||
*nestedError
|
||||
}
|
||||
Severity uint8
|
||||
)
|
||||
|
||||
@@ -25,20 +22,11 @@ const (
|
||||
SeverityWarning
|
||||
)
|
||||
|
||||
func (e errorInterface) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
func From(err error) NestedError {
|
||||
if IsNil(err) {
|
||||
return nil
|
||||
}
|
||||
switch err := err.(type) {
|
||||
case errorInterface:
|
||||
return err.nestedError
|
||||
default:
|
||||
return &nestedError{err: err}
|
||||
}
|
||||
return &nestedError{err: err}
|
||||
}
|
||||
|
||||
// Check is a helper function that
|
||||
@@ -112,7 +100,7 @@ func (ne NestedError) Error() error {
|
||||
if ne == nil {
|
||||
return nil
|
||||
}
|
||||
return errorInterface{ne}
|
||||
return ne.buildError(0, "")
|
||||
}
|
||||
|
||||
func (ne NestedError) With(s any) NestedError {
|
||||
@@ -123,10 +111,10 @@ func (ne NestedError) With(s any) NestedError {
|
||||
switch ss := s.(type) {
|
||||
case nil:
|
||||
return ne
|
||||
case *nestedError:
|
||||
return ne.withError(ss.Error())
|
||||
case error:
|
||||
case NestedError:
|
||||
return ne.withError(ss)
|
||||
case error:
|
||||
return ne.withError(From(ss))
|
||||
case string:
|
||||
msg = ss
|
||||
case fmt.Stringer:
|
||||
@@ -134,7 +122,7 @@ func (ne NestedError) With(s any) NestedError {
|
||||
default:
|
||||
msg = fmt.Sprint(s)
|
||||
}
|
||||
return ne.withError(errors.New(msg))
|
||||
return ne.withError(From(errors.New(msg)))
|
||||
}
|
||||
|
||||
func (ne NestedError) Extraf(format string, args ...any) NestedError {
|
||||
@@ -206,15 +194,17 @@ func errorf(format string, args ...any) NestedError {
|
||||
return From(fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
func (ne NestedError) withError(err error) NestedError {
|
||||
if ne != nil && IsNotNil(err) {
|
||||
ne.extras = append(ne.extras, *From(err))
|
||||
func (ne NestedError) withError(err NestedError) NestedError {
|
||||
if ne != nil && err != nil {
|
||||
ne.extras = append(ne.extras, *err)
|
||||
}
|
||||
return ne
|
||||
}
|
||||
|
||||
func (ne NestedError) writeToSB(sb *strings.Builder, level int, prefix string) {
|
||||
ne.writeIndents(sb, level)
|
||||
for i := 0; i < level; i++ {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
sb.WriteString(prefix)
|
||||
|
||||
if ne.NoError() {
|
||||
@@ -224,11 +214,7 @@ func (ne NestedError) writeToSB(sb *strings.Builder, level int, prefix string) {
|
||||
|
||||
sb.WriteString(ne.err.Error())
|
||||
if ne.subject != "" {
|
||||
if IsNotNil(ne.err) {
|
||||
sb.WriteString(fmt.Sprintf(" for %q", ne.subject))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprint(ne.subject))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" for %q", ne.subject))
|
||||
}
|
||||
if len(ne.extras) > 0 {
|
||||
sb.WriteRune(':')
|
||||
@@ -239,8 +225,32 @@ func (ne NestedError) writeToSB(sb *strings.Builder, level int, prefix string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ne NestedError) writeIndents(sb *strings.Builder, level int) {
|
||||
func (ne NestedError) buildError(level int, prefix string) error {
|
||||
var res error
|
||||
var sb strings.Builder
|
||||
|
||||
for i := 0; i < level; i++ {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
sb.WriteString(prefix)
|
||||
|
||||
if ne.NoError() {
|
||||
sb.WriteString("nil")
|
||||
return errors.New(sb.String())
|
||||
}
|
||||
|
||||
res = fmt.Errorf("%s%w", sb.String(), ne.err)
|
||||
sb.Reset()
|
||||
|
||||
if ne.subject != "" {
|
||||
sb.WriteString(fmt.Sprintf(" for %q", ne.subject))
|
||||
}
|
||||
if len(ne.extras) > 0 {
|
||||
sb.WriteRune(':')
|
||||
res = fmt.Errorf("%w%s", res, sb.String())
|
||||
for _, extra := range ne.extras {
|
||||
res = errors.Join(res, extra.buildError(level+1, "- "))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package error_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/error"
|
||||
@@ -17,6 +18,11 @@ func TestErrorIs(t *testing.T) {
|
||||
ExpectFalse(t, Invalid("foo", "bar").Is(ErrFailure))
|
||||
|
||||
ExpectFalse(t, Invalid("foo", "bar").Is(nil))
|
||||
|
||||
ExpectTrue(t, errors.Is(Failure("foo").Error(), ErrFailure))
|
||||
ExpectTrue(t, errors.Is(Failure("foo").With(Invalid("bar", "baz")).Error(), ErrInvalid))
|
||||
ExpectTrue(t, errors.Is(Failure("foo").With(Invalid("bar", "baz")).Error(), ErrFailure))
|
||||
ExpectFalse(t, errors.Is(Failure("foo").With(Invalid("bar", "baz")).Error(), ErrNotExists))
|
||||
}
|
||||
|
||||
func TestErrorNestedIs(t *testing.T) {
|
||||
@@ -99,4 +105,5 @@ func TestErrorNested(t *testing.T) {
|
||||
- 3
|
||||
- 3`
|
||||
ExpectEqual(t, ne.String(), want)
|
||||
ExpectEqual(t, ne.Error().Error(), want)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
R "github.com/yusing/go-proxy/route"
|
||||
"github.com/yusing/go-proxy/server"
|
||||
F "github.com/yusing/go-proxy/utils/functional"
|
||||
W "github.com/yusing/go-proxy/watcher"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -82,10 +83,18 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if args.Command == common.CommandDebugListEntries {
|
||||
printJSON(cfg.DumpEntries())
|
||||
return
|
||||
}
|
||||
|
||||
if err.HasError() {
|
||||
l.Warn(err)
|
||||
}
|
||||
|
||||
W.InitFileWatcherHelper()
|
||||
cfg.WatchChanges()
|
||||
|
||||
onShutdown.Add(docker.CloseAllClients)
|
||||
onShutdown.Add(cfg.Dispose)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ type (
|
||||
HideHeaders []string `yaml:"hide_headers" json:"hide_headers"` // http(s) proxy only
|
||||
|
||||
/* Docker only */
|
||||
*D.ProxyProperties `yaml:"-" json:"-"`
|
||||
*D.ProxyProperties `yaml:"-" json:"proxy_properties"`
|
||||
}
|
||||
|
||||
ProxyEntries = F.Map[string, *ProxyEntry]
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package proxy
|
||||
|
||||
var (
|
||||
PathMode_Forward = "forward"
|
||||
PathMode_RemovedPath = ""
|
||||
)
|
||||
|
||||
const (
|
||||
StreamType_UDP string = "udp"
|
||||
StreamType_TCP string = "tcp"
|
||||
@@ -19,4 +14,3 @@ var (
|
||||
HTTPSchemes = []string{"http", "https"}
|
||||
ValidSchemes = append(StreamSchemes, HTTPSchemes...)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
D "github.com/yusing/go-proxy/docker"
|
||||
E "github.com/yusing/go-proxy/error"
|
||||
@@ -14,6 +16,8 @@ type DockerProvider struct {
|
||||
dockerHost, hostname string
|
||||
}
|
||||
|
||||
var AliasRefRegex = regexp.MustCompile(`\$\d+`)
|
||||
|
||||
func DockerProviderImpl(dockerHost string) ProviderImpl {
|
||||
return &DockerProvider{dockerHost: dockerHost}
|
||||
}
|
||||
@@ -120,7 +124,7 @@ func (p *DockerProvider) entriesFromContainerLabels(container D.Container) (M.Pr
|
||||
|
||||
errors := E.NewBuilder("failed to apply label")
|
||||
for key, val := range container.Labels {
|
||||
errors.Add(p.applyLabel(entries, key, val))
|
||||
errors.Add(p.applyLabel(container, entries, key, val))
|
||||
}
|
||||
|
||||
// selecting correct host port
|
||||
@@ -132,9 +136,14 @@ func (p *DockerProvider) entriesFromContainerLabels(container D.Container) (M.Pr
|
||||
}
|
||||
for _, p := range container.Ports {
|
||||
containerPort := strconv.Itoa(int(p.PrivatePort))
|
||||
if containerPort == entry.Port {
|
||||
entry.Port = strconv.Itoa(int(p.PublicPort))
|
||||
publicPort := strconv.Itoa(int(p.PublicPort))
|
||||
entryPortSplit := strings.Split(entry.Port, ":")
|
||||
if len(entryPortSplit) == 2 && entryPortSplit[1] == containerPort {
|
||||
entryPortSplit[1] = publicPort
|
||||
} else if entryPortSplit[0] == containerPort {
|
||||
entryPortSplit[0] = publicPort
|
||||
}
|
||||
entry.Port = strings.Join(entryPortSplit, ":")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +151,7 @@ func (p *DockerProvider) entriesFromContainerLabels(container D.Container) (M.Pr
|
||||
return entries, errors.Build().Subject(container.ContainerName)
|
||||
}
|
||||
|
||||
func (p *DockerProvider) applyLabel(entries M.ProxyEntries, key, val string) (res E.NestedError) {
|
||||
func (p *DockerProvider) applyLabel(container D.Container, entries M.ProxyEntries, key, val string) (res E.NestedError) {
|
||||
b := E.NewBuilder("errors in label %s", key)
|
||||
defer b.To(&res)
|
||||
|
||||
@@ -161,6 +170,23 @@ func (p *DockerProvider) applyLabel(entries M.ProxyEntries, key, val string) (re
|
||||
}
|
||||
})
|
||||
} else {
|
||||
refErr := E.NewBuilder("errors parsing alias references")
|
||||
lbl.Target = AliasRefRegex.ReplaceAllStringFunc(lbl.Target, func(ref string) string {
|
||||
index, err := strconv.Atoi(ref[1:])
|
||||
if err != nil {
|
||||
refErr.Add(E.Invalid("integer", ref))
|
||||
return ref
|
||||
}
|
||||
if index < 1 || index > len(container.Aliases) {
|
||||
refErr.Add(E.Invalid("index", ref).Extraf("index out of range"))
|
||||
return ref
|
||||
}
|
||||
return container.Aliases[index-1]
|
||||
})
|
||||
if refErr.HasError() {
|
||||
b.Add(refErr.Build())
|
||||
return
|
||||
}
|
||||
config, ok := entries.Load(lbl.Target)
|
||||
if !ok {
|
||||
b.Add(E.NotExist("alias", lbl.Target))
|
||||
|
||||
145
src/proxy/provider/docker_provider_test.go
Normal file
145
src/proxy/provider/docker_provider_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
D "github.com/yusing/go-proxy/docker"
|
||||
E "github.com/yusing/go-proxy/error"
|
||||
F "github.com/yusing/go-proxy/utils/functional"
|
||||
. "github.com/yusing/go-proxy/utils/testing"
|
||||
)
|
||||
|
||||
func get[KT comparable, VT any](m F.Map[KT, VT], key KT) VT {
|
||||
v, _ := m.Load(key)
|
||||
return v
|
||||
}
|
||||
|
||||
var dummyNames = []string{"/a"}
|
||||
|
||||
func TestApplyLabelFieldValidity(t *testing.T) {
|
||||
pathPatterns := `
|
||||
- /
|
||||
- POST /upload/{$}
|
||||
- GET /static
|
||||
`[1:]
|
||||
pathPatternsExpect := []string{
|
||||
"/",
|
||||
"POST /upload/{$}",
|
||||
"GET /static",
|
||||
}
|
||||
setHeaders := `
|
||||
X_Custom_Header1: value1
|
||||
X_Custom_Header1: value2
|
||||
X_Custom_Header2: value3
|
||||
`[1:]
|
||||
setHeadersExpect := map[string]string{
|
||||
"X_Custom_Header1": "value1, value2",
|
||||
"X_Custom_Header2": "value3",
|
||||
}
|
||||
hideHeaders := `
|
||||
- X-Custom-Header1
|
||||
- X-Custom-Header2
|
||||
`[1:]
|
||||
hideHeadersExpect := []string{
|
||||
"X-Custom-Header1",
|
||||
"X-Custom-Header2",
|
||||
}
|
||||
var p DockerProvider
|
||||
var c = D.FromDocker(&types.Container{
|
||||
Names: dummyNames,
|
||||
Labels: map[string]string{
|
||||
D.LableAliases: "a,b",
|
||||
"proxy.*.scheme": "https",
|
||||
"proxy.*.host": "app",
|
||||
"proxy.*.port": "4567",
|
||||
"proxy.a.no_tls_verify": "true",
|
||||
"proxy.a.path_patterns": pathPatterns,
|
||||
"proxy.a.set_headers": setHeaders,
|
||||
"proxy.a.hide_headers": hideHeaders,
|
||||
}}, "")
|
||||
entries, err := p.entriesFromContainerLabels(c)
|
||||
ExpectNoError(t, err.Error())
|
||||
a := get(entries, "a")
|
||||
b := get(entries, "b")
|
||||
|
||||
ExpectEqual(t, a.Scheme, "https")
|
||||
ExpectEqual(t, b.Scheme, "https")
|
||||
|
||||
ExpectEqual(t, a.Host, "app")
|
||||
ExpectEqual(t, b.Host, "app")
|
||||
|
||||
ExpectEqual(t, a.Port, "4567")
|
||||
ExpectEqual(t, b.Port, "4567")
|
||||
|
||||
ExpectEqual(t, a.NoTLSVerify, true)
|
||||
ExpectEqual(t, b.NoTLSVerify, false)
|
||||
|
||||
ExpectDeepEqual(t, a.PathPatterns, pathPatternsExpect)
|
||||
ExpectEqual(t, len(b.PathPatterns), 0)
|
||||
|
||||
ExpectDeepEqual(t, a.SetHeaders, setHeadersExpect)
|
||||
ExpectEqual(t, len(b.SetHeaders), 0)
|
||||
|
||||
ExpectDeepEqual(t, a.HideHeaders, hideHeadersExpect)
|
||||
ExpectEqual(t, len(b.HideHeaders), 0)
|
||||
}
|
||||
|
||||
func TestApplyLabel(t *testing.T) {
|
||||
var p DockerProvider
|
||||
var c = D.FromDocker(&types.Container{
|
||||
Names: dummyNames,
|
||||
Labels: map[string]string{
|
||||
D.LableAliases: "a,b,c",
|
||||
"proxy.a.no_tls_verify": "true",
|
||||
"proxy.b.port": "1234",
|
||||
"proxy.c.scheme": "https",
|
||||
}}, "")
|
||||
entries, err := p.entriesFromContainerLabels(c)
|
||||
ExpectNoError(t, err.Error())
|
||||
ExpectEqual(t, get(entries, "a").NoTLSVerify, true)
|
||||
ExpectEqual(t, get(entries, "b").Port, "1234")
|
||||
ExpectEqual(t, get(entries, "c").Scheme, "https")
|
||||
}
|
||||
|
||||
func TestApplyLabelWithRef(t *testing.T) {
|
||||
var p DockerProvider
|
||||
var c = D.FromDocker(&types.Container{
|
||||
Names: dummyNames,
|
||||
Labels: map[string]string{
|
||||
D.LableAliases: "a,b,c",
|
||||
"proxy.$1.host": "localhost",
|
||||
"proxy.$2.port": "1234",
|
||||
"proxy.$3.scheme": "https",
|
||||
}}, "")
|
||||
entries, err := p.entriesFromContainerLabels(c)
|
||||
ExpectNoError(t, err.Error())
|
||||
ExpectEqual(t, get(entries, "a").Host, "localhost")
|
||||
ExpectEqual(t, get(entries, "b").Port, "1234")
|
||||
ExpectEqual(t, get(entries, "c").Scheme, "https")
|
||||
}
|
||||
|
||||
func TestApplyLabelWithRefIndexError(t *testing.T) {
|
||||
var p DockerProvider
|
||||
var c = D.FromDocker(&types.Container{
|
||||
Names: dummyNames,
|
||||
Labels: map[string]string{
|
||||
D.LableAliases: "a,b",
|
||||
"proxy.$1.host": "localhost",
|
||||
"proxy.$4.scheme": "https",
|
||||
}}, "")
|
||||
_, err := p.entriesFromContainerLabels(c)
|
||||
ExpectError(t, E.ErrInvalid, err.Error())
|
||||
ExpectTrue(t, strings.Contains(err.String(), "index out of range"))
|
||||
|
||||
c = D.FromDocker(&types.Container{
|
||||
Names: dummyNames,
|
||||
Labels: map[string]string{
|
||||
D.LableAliases: "a,b",
|
||||
"proxy.$0.host": "localhost",
|
||||
}}, "")
|
||||
_, err = p.entriesFromContainerLabels(c)
|
||||
ExpectError(t, E.ErrInvalid, err.Error())
|
||||
ExpectTrue(t, strings.Contains(err.String(), "index out of range"))
|
||||
}
|
||||
@@ -168,4 +168,5 @@ var (
|
||||
|
||||
httpRoutes = F.NewMapOf[SubdomainKey, *HTTPRoute]()
|
||||
httpRoutesMu sync.Mutex
|
||||
globalMux = http.NewServeMux()
|
||||
)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/error"
|
||||
)
|
||||
|
||||
func snakeToPascal(s string) string {
|
||||
toHyphenCamel := http.CanonicalHeaderKey(strings.ReplaceAll(s, "_", "-"))
|
||||
return strings.ReplaceAll(toHyphenCamel, "-", "")
|
||||
}
|
||||
|
||||
func SetFieldFromSnake[T, VT any](obj *T, field string, value VT) E.NestedError {
|
||||
field = snakeToPascal(field)
|
||||
prop := reflect.ValueOf(obj).Elem().FieldByName(field)
|
||||
if prop.Kind() == 0 {
|
||||
return E.Invalid("field", field)
|
||||
}
|
||||
prop.Set(reflect.ValueOf(value))
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -12,6 +13,13 @@ func ExpectNoError(t *testing.T, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectError(t *testing.T, expected error, err error) {
|
||||
t.Helper()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Errorf("expected err %s, got nil", expected.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectEqual[T comparable](t *testing.T, got T, want T) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
|
||||
@@ -23,4 +23,8 @@ func (f *fileWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.Nested
|
||||
return fwHelper.Add(ctx, f)
|
||||
}
|
||||
|
||||
var fwHelper = newFileWatcherHelper(common.ConfigBasePath)
|
||||
func InitFileWatcherHelper() {
|
||||
fwHelper = newFileWatcherHelper(common.ConfigBasePath)
|
||||
}
|
||||
|
||||
var fwHelper *fileWatcherHelper
|
||||
|
||||
Reference in New Issue
Block a user