Skip to content

Commit

Permalink
smarter scheme and port detection
Browse files Browse the repository at this point in the history
  • Loading branch information
yusing committed Sep 23, 2024
1 parent 8e2cc56 commit 71e8e4a
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 129 deletions.
78 changes: 46 additions & 32 deletions src/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,44 @@ import (
)

type ProxyProperties struct {
DockerHost string `yaml:"-" json:"docker_host"`
ContainerName string `yaml:"-" json:"container_name"`
ImageName string `yaml:"-" json:"image_name"`
Aliases []string `yaml:"-" json:"aliases"`
IsExcluded bool `yaml:"-" json:"is_excluded"`
FirstPort string `yaml:"-" json:"first_port"`
IdleTimeout string `yaml:"-" json:"idle_timeout"`
WakeTimeout string `yaml:"-" json:"wake_timeout"`
StopMethod string `yaml:"-" json:"stop_method"`
StopTimeout string `yaml:"-" json:"stop_timeout"` // stop_method = "stop" only
StopSignal string `yaml:"-" json:"stop_signal"` // stop_method = "stop" | "kill" only
Running bool `yaml:"-" json:"running"`
DockerHost string `yaml:"-" json:"docker_host"`
ContainerName string `yaml:"-" json:"container_name"`
ImageName string `yaml:"-" json:"image_name"`
PublicPortMapping PortMapping `yaml:"-" json:"public_port_mapping"` // non-zero publicPort:types.Port
PrivatePortMapping PortMapping `yaml:"-" json:"private_port_mapping"` // privatePort:types.Port
Aliases []string `yaml:"-" json:"aliases"`
IsExcluded bool `yaml:"-" json:"is_excluded"`
IdleTimeout string `yaml:"-" json:"idle_timeout"`
WakeTimeout string `yaml:"-" json:"wake_timeout"`
StopMethod string `yaml:"-" json:"stop_method"`
StopTimeout string `yaml:"-" json:"stop_timeout"` // stop_method = "stop" only
StopSignal string `yaml:"-" json:"stop_signal"` // stop_method = "stop" | "kill" only
Running bool `yaml:"-" json:"running"`
}

type Container struct {
*types.Container
*ProxyProperties
}

type PortMapping = map[string]types.Port

func FromDocker(c *types.Container, dockerHost string) (res Container) {
res.Container = c
res.ProxyProperties = &ProxyProperties{
DockerHost: dockerHost,
ContainerName: res.getName(),
ImageName: res.getImageName(),
Aliases: res.getAliases(),
IsExcluded: U.ParseBool(res.getDeleteLabel(LableExclude)),
FirstPort: res.firstPortOrEmpty(),
IdleTimeout: res.getDeleteLabel(LabelIdleTimeout),
WakeTimeout: res.getDeleteLabel(LabelWakeTimeout),
StopMethod: res.getDeleteLabel(LabelStopMethod),
StopTimeout: res.getDeleteLabel(LabelStopTimeout),
StopSignal: res.getDeleteLabel(LabelStopSignal),
Running: c.Status == "running" || c.State == "running",
DockerHost: dockerHost,
ContainerName: res.getName(),
ImageName: res.getImageName(),
PublicPortMapping: res.getPublicPortMapping(),
PrivatePortMapping: res.getPrivatePortMapping(),
Aliases: res.getAliases(),
IsExcluded: U.ParseBool(res.getDeleteLabel(LabelExclude)),
IdleTimeout: res.getDeleteLabel(LabelIdleTimeout),
WakeTimeout: res.getDeleteLabel(LabelWakeTimeout),
StopMethod: res.getDeleteLabel(LabelStopMethod),
StopTimeout: res.getDeleteLabel(LabelStopTimeout),
StopSignal: res.getDeleteLabel(LabelStopSignal),
Running: c.Status == "running" || c.State == "running",
}
return
}
Expand Down Expand Up @@ -81,7 +85,7 @@ func (c Container) getDeleteLabel(label string) string {
}

func (c Container) getAliases() []string {
if l := c.getDeleteLabel(LableAliases); l != "" {
if l := c.getDeleteLabel(LabelAliases); l != "" {
return U.CommaSeperatedList(l)
} else {
return []string{c.getName()}
Expand All @@ -98,14 +102,24 @@ func (c Container) getImageName() string {
return slashSep[len(slashSep)-1]
}

func (c Container) firstPortOrEmpty() string {
if len(c.Ports) == 0 {
return ""
func (c Container) getPublicPortMapping() PortMapping {
res := make(PortMapping)
for _, v := range c.Ports {
if v.PublicPort == 0 {
continue
}
res[fmt.Sprint(v.PublicPort)] = v
}
for _, p := range c.Ports {
if p.PublicPort != 0 {
return fmt.Sprint(p.PublicPort)
return res
}

func (c Container) getPrivatePortMapping() PortMapping {
res := make(PortMapping)
for _, v := range c.Ports {
if v.PublicPort == 0 {
continue
}
res[fmt.Sprint(v.PrivatePort)] = v
}
return ""
return res
}
4 changes: 2 additions & 2 deletions src/docker/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package docker
const (
WildcardAlias = "*"

LableAliases = NSProxy + ".aliases"
LableExclude = NSProxy + ".exclude"
LabelAliases = NSProxy + ".aliases"
LabelExclude = NSProxy + ".exclude"
LabelIdleTimeout = NSProxy + ".idle_timeout"
LabelWakeTimeout = NSProxy + ".wake_timeout"
LabelStopMethod = NSProxy + ".stop_method"
Expand Down
12 changes: 10 additions & 2 deletions src/error/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ func (ne NestedError) With(s any) NestedError {
case string:
msg = ss
case fmt.Stringer:
msg = ss.String()
return ne.append(ss.String())
default:
msg = fmt.Sprint(s)
return ne.append(fmt.Sprint(s))
}
return ne.withError(From(errors.New(msg)))
}
Expand Down Expand Up @@ -201,6 +201,14 @@ func (ne NestedError) withError(err NestedError) NestedError {
return ne
}

func (ne NestedError) append(msg string) NestedError {
if ne == nil {
return nil
}
ne.err = fmt.Errorf("%w %s", ne.err, msg)
return ne
}

func (ne NestedError) writeToSB(sb *strings.Builder, level int, prefix string) {
for i := 0; i < level; i++ {
sb.WriteString(" ")
Expand Down
30 changes: 24 additions & 6 deletions src/models/raw_entry.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"fmt"
"strconv"
"strings"

Expand Down Expand Up @@ -53,11 +54,17 @@ func (e *RawEntry) FillMissingFields() bool {
}
}

if e.Port == "" {
if e.FirstPort == "" {
return false
if e.PublicPortMapping != nil {
if _, ok := e.PublicPortMapping[e.Port]; !ok { // port is not exposed, but specified
// try to fallback to first public port
if len(e.PublicPortMapping) == 0 {
return false
}
for _, p := range e.PublicPortMapping {
e.Port = fmt.Sprint(p.PublicPort)
break
}
}
e.Port = e.FirstPort
}

if e.Scheme == "" {
Expand All @@ -69,8 +76,19 @@ func (e *RawEntry) FillMissingFields() bool {
e.Scheme = "http"
} else if e.Port == "443" {
e.Scheme = "https"
} else if isDocker && e.Port == "" {
return false
} else if isDocker {
if e.Port == "" {
return false
}
if p, ok := e.PublicPortMapping[e.Port]; ok {
if p.Type == "udp" {
e.Scheme = "udp"
} else {
e.Scheme = "http"
}
} else {
return false
}
} else {
e.Scheme = "http"
}
Expand Down
16 changes: 0 additions & 16 deletions src/proxy/constants.go

This file was deleted.

4 changes: 4 additions & 0 deletions src/proxy/fields/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (p Port) inBound() bool {
return p >= MinPort && p <= MaxPort
}

func (p Port) String() string {
return strconv.Itoa(int(p))
}

const (
MinPort = 0
MaxPort = 65535
Expand Down
2 changes: 1 addition & 1 deletion src/proxy/fields/stream_port.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func ValidateStreamPort(p string) (StreamPort, E.NestedError) {
} else if err != nil {
proxyPort, err = parseNameToPort(split[1])
if err != nil {
return ErrStreamPort, err
return ErrStreamPort, E.Invalid("stream port", p).With(proxyPort)
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/proxy/fields/stream_port_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"testing"

E "github.com/yusing/go-proxy/error"
U "github.com/yusing/go-proxy/utils/testing"
. "github.com/yusing/go-proxy/utils/testing"
)

var validPorts = []string{
Expand Down Expand Up @@ -35,14 +35,14 @@ var outOfRangePorts = []string{
func TestStreamPort(t *testing.T) {
for _, port := range validPorts {
_, err := ValidateStreamPort(port)
U.ExpectNoError(t, err.Error())
ExpectNoError(t, err.Error())
}
for _, port := range invalidPorts {
_, err := ValidateStreamPort(port)
U.ExpectError2(t, port, E.ErrInvalid, err.Error())
ExpectError2(t, port, E.ErrInvalid, err.Error())
}
for _, port := range outOfRangePorts {
_, err := ValidateStreamPort(port)
U.ExpectError2(t, port, E.ErrOutOfRange, err.Error())
ExpectError2(t, port, E.ErrOutOfRange, err.Error())
}
}
36 changes: 17 additions & 19 deletions src/proxy/provider/docker_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,31 +137,29 @@ func (p *DockerProvider) entriesFromContainerLabels(container D.Container) (M.Ra
errors.Add(p.applyLabel(container, entries, key, val))
}

// remove all entries that failed to fill in missing fields
entries.RemoveAll(func(re *M.RawEntry) bool {
return !re.FillMissingFields()
})

// selecting correct host port
if container.HostConfig.NetworkMode != "host" {
for _, a := range container.Aliases {
entry, ok := entries.Load(a)
if !ok {
continue
}
for _, p := range container.Ports {
containerPort := strconv.Itoa(int(p.PrivatePort))
publicPort := strconv.Itoa(int(p.PublicPort))
replacePrivPorts := func() {
if container.HostConfig.NetworkMode != "host" {
entries.RangeAll(func(_ string, entry *M.RawEntry) {
entryPortSplit := strings.Split(entry.Port, ":")
n := len(entryPortSplit)
if entryPortSplit[n-1] == containerPort {
entryPortSplit[n-1] = publicPort
// if the port matches the proxy port, replace it with the public port
if p, ok := container.PrivatePortMapping[entryPortSplit[n-1]]; ok {
entryPortSplit[n-1] = fmt.Sprint(p.PublicPort)
entry.Port = strings.Join(entryPortSplit, ":")
break
}
}
})
}
}
replacePrivPorts()

// remove all entries that failed to fill in missing fields
entries.RemoveAll(func(re *M.RawEntry) bool {
return !re.FillMissingFields()
})

// do it again since the port may got filled in
replacePrivPorts()

return entries, errors.Build().Subject(container.ContainerName)
}
Expand Down Expand Up @@ -193,7 +191,7 @@ func (p *DockerProvider) applyLabel(container D.Container, entries M.RawEntries,
return ref
}
if index < 1 || index > len(container.Aliases) {
refErr.Add(E.Invalid("index", ref).Extraf("index out of range"))
refErr.Add(E.OutOfRange("index", ref))
return ref
}
return container.Aliases[index-1]
Expand Down
Loading

0 comments on commit 71e8e4a

Please sign in to comment.