From b38d7595a77faaf4ec787bae8d1f4e2667f5d818 Mon Sep 17 00:00:00 2001 From: yusing Date: Mon, 30 Sep 2024 15:19:59 +0800 Subject: [PATCH] fixed issue for container not being excluded on restart --- Makefile | 2 +- internal/common/constants.go | 2 + internal/docker/container.go | 1 + internal/docker/idlewatcher/watcher.go | 26 ++++++----- internal/docker/proxy_properties.go | 1 + internal/error/builder.go | 2 - .../net/http/middleware/cloudflare_real_ip.go | 2 +- internal/net/http/middleware/forward_auth.go | 44 +++++++++---------- internal/net/http/middleware/middleware.go | 4 +- .../net/http/middleware/modify_request.go | 4 +- .../net/http/middleware/modify_response.go | 4 +- internal/net/http/middleware/real_ip.go | 4 +- internal/proxy/entry.go | 2 + internal/proxy/provider/docker.go | 15 +++++-- internal/route/http.go | 2 +- internal/watcher/docker_watcher.go | 4 +- 16 files changed, 67 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 7f730ba..2b33b8f 100755 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ logs: docker compose logs -f get: - cd cmd && go get -u && go mod tidy && cd .. + go get -u ./cmd && go mod tidy debug: make BUILD_FLAG="" build && sudo GOPROXY_DEBUG=1 bin/go-proxy diff --git a/internal/common/constants.go b/internal/common/constants.go index 4b69e66..961b795 100644 --- a/internal/common/constants.go +++ b/internal/common/constants.go @@ -17,6 +17,8 @@ const ( ConfigFileName = "config.yml" ConfigExampleFileName = "config.example.yml" ConfigPath = ConfigBasePath + "/" + ConfigFileName + + MiddlewareDefsBasePath = ConfigBasePath + "/middlewares" ) const ( diff --git a/internal/docker/container.go b/internal/docker/container.go index 1dcbc9c..04a83f4 100644 --- a/internal/docker/container.go +++ b/internal/docker/container.go @@ -20,6 +20,7 @@ func FromDocker(c *types.Container, dockerHost string) (res Container) { res.ProxyProperties = &ProxyProperties{ DockerHost: dockerHost, ContainerName: res.getName(), + ContainerID: c.ID, ImageName: res.getImageName(), PublicPortMapping: res.getPublicPortMapping(), PrivatePortMapping: res.getPrivatePortMapping(), diff --git a/internal/docker/idlewatcher/watcher.go b/internal/docker/idlewatcher/watcher.go index 4fa5cd9..bb54166 100644 --- a/internal/docker/idlewatcher/watcher.go +++ b/internal/docker/idlewatcher/watcher.go @@ -63,7 +63,9 @@ func Register(entry *P.ReverseProxyEntry) (*watcher, E.NestedError) { watcherMapMu.Lock() defer watcherMapMu.Unlock() - if w, ok := watcherMap[entry.ContainerName]; ok { + key := entry.ContainerID + + if w, ok := watcherMap[key]; ok { w.refCount.Add(1) w.ReverseProxyEntry = entry return w, nil @@ -85,7 +87,7 @@ func Register(entry *P.ReverseProxyEntry) (*watcher, E.NestedError) { w.refCount.Add(1) w.stopByMethod = w.getStopCallback() - watcherMap[w.ContainerName] = w + watcherMap[key] = w go func() { newWatcherCh <- w @@ -94,8 +96,8 @@ func Register(entry *P.ReverseProxyEntry) (*watcher, E.NestedError) { return w, nil } -func Unregister(containerName string) { - if w, ok := watcherMap[containerName]; ok { +func Unregister(entry *P.ReverseProxyEntry) { + if w, ok := watcherMap[entry.ContainerID]; ok { w.refCount.Add(-1) } } @@ -118,7 +120,7 @@ func Start() { w.refCount.Wait() // wait for 0 ref count w.client.Close() - delete(watcherMap, w.ContainerName) + delete(watcherMap, w.ContainerID) w.l.Debug("unregistered") mainLoopWg.Done() }() @@ -138,29 +140,29 @@ func (w *watcher) PatchRoundTripper(rtp http.RoundTripper) roundTripper { } func (w *watcher) containerStop() error { - return w.client.ContainerStop(w.ctx, w.ContainerName, container.StopOptions{ + return w.client.ContainerStop(w.ctx, w.ContainerID, container.StopOptions{ Signal: string(w.StopSignal), Timeout: &w.StopTimeout}) } func (w *watcher) containerPause() error { - return w.client.ContainerPause(w.ctx, w.ContainerName) + return w.client.ContainerPause(w.ctx, w.ContainerID) } func (w *watcher) containerKill() error { - return w.client.ContainerKill(w.ctx, w.ContainerName, string(w.StopSignal)) + return w.client.ContainerKill(w.ctx, w.ContainerID, string(w.StopSignal)) } func (w *watcher) containerUnpause() error { - return w.client.ContainerUnpause(w.ctx, w.ContainerName) + return w.client.ContainerUnpause(w.ctx, w.ContainerID) } func (w *watcher) containerStart() error { - return w.client.ContainerStart(w.ctx, w.ContainerName, container.StartOptions{}) + return w.client.ContainerStart(w.ctx, w.ContainerID, container.StartOptions{}) } func (w *watcher) containerStatus() (string, E.NestedError) { - json, err := w.client.ContainerInspect(w.ctx, w.ContainerName) + json, err := w.client.ContainerInspect(w.ctx, w.ContainerID) if err != nil { return "", E.FailWith("inspect container", err) } @@ -219,7 +221,7 @@ func (w *watcher) watchUntilCancel() { dockerEventCh, dockerEventErrCh := dockerWatcher.EventsWithOptions(w.ctx, W.DockerListOptions{ Filters: W.NewDockerFilter( W.DockerFilterContainer, - W.DockerrFilterContainerName(w.ContainerName), + W.DockerrFilterContainer(w.ContainerID), W.DockerFilterStart, W.DockerFilterStop, W.DockerFilterDie, diff --git a/internal/docker/proxy_properties.go b/internal/docker/proxy_properties.go index b7a68f3..d86b499 100644 --- a/internal/docker/proxy_properties.go +++ b/internal/docker/proxy_properties.go @@ -6,6 +6,7 @@ type PortMapping = map[string]types.Port type ProxyProperties struct { DockerHost string `yaml:"-" json:"docker_host"` ContainerName string `yaml:"-" json:"container_name"` + ContainerID string `yaml:"-" json:"container_id"` 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 diff --git a/internal/error/builder.go b/internal/error/builder.go index 086287d..813b90c 100644 --- a/internal/error/builder.go +++ b/internal/error/builder.go @@ -49,8 +49,6 @@ func (b Builder) Addf(format string, args ...any) Builder { func (b Builder) Build() NestedError { if len(b.errors) == 0 { return nil - } else if len(b.errors) == 1 { - return b.errors[0].Subjectf("%s", b.message) } return Join(b.message, b.errors...) } diff --git a/internal/net/http/middleware/cloudflare_real_ip.go b/internal/net/http/middleware/cloudflare_real_ip.go index 8f07ec1..1bd83d9 100644 --- a/internal/net/http/middleware/cloudflare_real_ip.go +++ b/internal/net/http/middleware/cloudflare_real_ip.go @@ -34,7 +34,7 @@ var CloudflareRealIP = &realIP{ }, } -func NewCloudflareRealIP(_ OptionsRaw, _ *ReverseProxy) (*Middleware, E.NestedError) { +func NewCloudflareRealIP(_ OptionsRaw) (*Middleware, E.NestedError) { cri := new(realIP) cri.m = &Middleware{ impl: cri, diff --git a/internal/net/http/middleware/forward_auth.go b/internal/net/http/middleware/forward_auth.go index 27256bc..ba5b8bc 100644 --- a/internal/net/http/middleware/forward_auth.go +++ b/internal/net/http/middleware/forward_auth.go @@ -14,7 +14,6 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/yusing/go-proxy/internal/common" D "github.com/yusing/go-proxy/internal/docker" E "github.com/yusing/go-proxy/internal/error" gpHTTP "github.com/yusing/go-proxy/internal/net/http" @@ -31,6 +30,7 @@ type ( TrustForwardHeader bool AuthResponseHeaders []string AddAuthCookiesToResponse []string + transport http.RoundTripper } ) @@ -56,16 +56,29 @@ var ForwardAuth = func() *forwardAuth { }() var faLogger = logrus.WithField("middleware", "ForwardAuth") -func NewForwardAuthfunc(optsRaw OptionsRaw, rp *ReverseProxy) (*Middleware, E.NestedError) { - tr, ok := rp.Transport.(*http.Transport) - if ok { - tr = tr.Clone() - } else { - tr = common.DefaultTransport.Clone() - } - +func NewForwardAuthfunc(optsRaw OptionsRaw) (*Middleware, E.NestedError) { faWithOpts := new(forwardAuth) faWithOpts.forwardAuthOpts = new(forwardAuthOpts) + err := Deserialize(optsRaw, faWithOpts.forwardAuthOpts) + if err != nil { + return nil, err + } + _, err = E.Check(url.Parse(faWithOpts.Address)) + if err != nil { + return nil, E.Invalid("address", faWithOpts.Address) + } + + faWithOpts.m = &Middleware{ + impl: faWithOpts, + before: faWithOpts.forward, + } + + // TODO: use tr from reverse proxy + tr, ok := faWithOpts.forwardAuthOpts.transport.(*http.Transport) + if ok { + tr = tr.Clone() + } + faWithOpts.client = http.Client{ CheckRedirect: func(r *Request, via []*Request) error { return http.ErrUseLastResponse @@ -73,19 +86,6 @@ func NewForwardAuthfunc(optsRaw OptionsRaw, rp *ReverseProxy) (*Middleware, E.Ne Timeout: 30 * time.Second, Transport: tr, } - faWithOpts.m = &Middleware{ - impl: faWithOpts, - before: faWithOpts.forward, - } - - err := Deserialize(optsRaw, faWithOpts.forwardAuthOpts) - if err != nil { - return nil, E.FailWith("set options", err) - } - _, err = E.Check(url.Parse(faWithOpts.Address)) - if err != nil { - return nil, E.Invalid("address", faWithOpts.Address) - } return faWithOpts.m, nil } diff --git a/internal/net/http/middleware/middleware.go b/internal/net/http/middleware/middleware.go index 2737d67..249d079 100644 --- a/internal/net/http/middleware/middleware.go +++ b/internal/net/http/middleware/middleware.go @@ -23,7 +23,7 @@ type ( BeforeFunc func(next http.Handler, w ResponseWriter, r *Request) RewriteFunc func(req *Request) ModifyResponseFunc func(resp *Response) error - CloneWithOptFunc func(opts OptionsRaw, rp *ReverseProxy) (*Middleware, E.NestedError) + CloneWithOptFunc func(opts OptionsRaw) (*Middleware, E.NestedError) OptionsRaw = map[string]any Options any @@ -55,7 +55,7 @@ func (m *Middleware) String() string { func (m *Middleware) WithOptionsClone(optsRaw OptionsRaw, rp *ReverseProxy) (*Middleware, E.NestedError) { if len(optsRaw) != 0 && m.withOptions != nil { - if mWithOpt, err := m.withOptions(optsRaw, rp); err != nil { + if mWithOpt, err := m.withOptions(optsRaw); err != nil { return nil, err } else { return mWithOpt, nil diff --git a/internal/net/http/middleware/modify_request.go b/internal/net/http/middleware/modify_request.go index b497459..f36fe07 100644 --- a/internal/net/http/middleware/modify_request.go +++ b/internal/net/http/middleware/modify_request.go @@ -30,7 +30,7 @@ var ModifyRequest = func() *modifyRequest { return mr }() -func NewModifyRequest(optsRaw OptionsRaw, _ *ReverseProxy) (*Middleware, E.NestedError) { +func NewModifyRequest(optsRaw OptionsRaw) (*Middleware, E.NestedError) { mr := new(modifyRequest) mr.m = &Middleware{ impl: mr, @@ -39,7 +39,7 @@ func NewModifyRequest(optsRaw OptionsRaw, _ *ReverseProxy) (*Middleware, E.Neste mr.modifyRequestOpts = new(modifyRequestOpts) err := Deserialize(optsRaw, mr.modifyRequestOpts) if err != nil { - return nil, E.FailWith("set options", err) + return nil, err } return mr.m, nil } diff --git a/internal/net/http/middleware/modify_response.go b/internal/net/http/middleware/modify_response.go index 1a77d65..a212f0c 100644 --- a/internal/net/http/middleware/modify_response.go +++ b/internal/net/http/middleware/modify_response.go @@ -32,7 +32,7 @@ var ModifyResponse = func() (mr *modifyResponse) { return }() -func NewModifyResponse(optsRaw OptionsRaw, _ *ReverseProxy) (*Middleware, E.NestedError) { +func NewModifyResponse(optsRaw OptionsRaw) (*Middleware, E.NestedError) { mr := new(modifyResponse) mr.m = &Middleware{ impl: mr, @@ -41,7 +41,7 @@ func NewModifyResponse(optsRaw OptionsRaw, _ *ReverseProxy) (*Middleware, E.Nest mr.modifyResponseOpts = new(modifyResponseOpts) err := Deserialize(optsRaw, mr.modifyResponseOpts) if err != nil { - return nil, E.FailWith("set options", err) + return nil, err } return mr.m, nil } diff --git a/internal/net/http/middleware/real_ip.go b/internal/net/http/middleware/real_ip.go index b624f8c..42e4c6c 100644 --- a/internal/net/http/middleware/real_ip.go +++ b/internal/net/http/middleware/real_ip.go @@ -58,7 +58,7 @@ var realIPOptsDefault = func() *realIPOpts { var realIPLogger = logrus.WithField("middleware", "RealIP") -func NewRealIP(opts OptionsRaw, _ *ReverseProxy) (*Middleware, E.NestedError) { +func NewRealIP(opts OptionsRaw) (*Middleware, E.NestedError) { riWithOpts := new(realIP) riWithOpts.m = &Middleware{ impl: riWithOpts, @@ -67,7 +67,7 @@ func NewRealIP(opts OptionsRaw, _ *ReverseProxy) (*Middleware, E.NestedError) { riWithOpts.realIPOpts = realIPOptsDefault() err := Deserialize(opts, riWithOpts.realIPOpts) if err != nil { - return nil, E.FailWith("set options", err) + return nil, err } return riWithOpts.m, nil } diff --git a/internal/proxy/entry.go b/internal/proxy/entry.go index 3d0faac..7b79ec0 100644 --- a/internal/proxy/entry.go +++ b/internal/proxy/entry.go @@ -28,6 +28,7 @@ type ( StopSignal T.Signal DockerHost string ContainerName string + ContainerID string ContainerRunning bool } StreamEntry struct { @@ -115,6 +116,7 @@ func validateRPEntry(m *M.RawEntry, s T.Scheme, b E.Builder) *ReverseProxyEntry StopSignal: stopSignal, DockerHost: m.DockerHost, ContainerName: m.ContainerName, + ContainerID: m.ContainerID, ContainerRunning: m.Running, } } diff --git a/internal/proxy/provider/docker.go b/internal/proxy/provider/docker.go index 6cf52f0..dbe74cd 100755 --- a/internal/proxy/provider/docker.go +++ b/internal/proxy/provider/docker.go @@ -78,12 +78,17 @@ func (p *DockerProvider) LoadRoutesImpl() (routes R.Routes, err E.NestedError) { return routes, errors.Build() } +func (p *DockerProvider) shouldIgnore(container D.Container) bool { + return container.IsExcluded || + !container.IsExplicit && p.ExplicitOnly +} + func (p *DockerProvider) OnEvent(event W.Event, routes R.Routes) (res EventResult) { b := E.NewBuilder("event %s error", event) defer b.To(&res.err) routes.RangeAll(func(k string, v R.Route) { - if v.Entry().ContainerName == event.ActorName { + if v.Entry().ContainerID == event.ActorID { b.Add(v.Stop()) routes.Delete(k) res.nRemoved++ @@ -101,6 +106,11 @@ func (p *DockerProvider) OnEvent(event W.Event, routes R.Routes) (res EventResul b.Add(E.FailWith("inspect container", err)) return } + + if p.shouldIgnore(cont) { + return + } + entries, err := p.entriesFromContainerLabels(cont) b.Add(err) @@ -126,8 +136,7 @@ func (p *DockerProvider) OnEvent(event W.Event, routes R.Routes) (res EventResul func (p *DockerProvider) entriesFromContainerLabels(container D.Container) (entries M.RawEntries, _ E.NestedError) { entries = M.NewProxyEntries() - if container.IsExcluded || - !container.IsExplicit && p.ExplicitOnly { + if p.shouldIgnore(container) { return } diff --git a/internal/route/http.go b/internal/route/http.go index b24b0b0..068810e 100755 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -89,7 +89,7 @@ func NewHTTPRoute(entry *P.ReverseProxyEntry) (*HTTPRoute, E.NestedError) { return nil } unregIdleWatcher = func() { - idlewatcher.Unregister(entry.ContainerName) + idlewatcher.Unregister(entry) rp.Transport = trans } } diff --git a/internal/watcher/docker_watcher.go b/internal/watcher/docker_watcher.go index d9c6a99..45c39e9 100644 --- a/internal/watcher/docker_watcher.go +++ b/internal/watcher/docker_watcher.go @@ -37,8 +37,8 @@ var ( dockerWatcherRetryInterval = 3 * time.Second ) -func DockerrFilterContainerName(name string) filters.KeyValuePair { - return filters.Arg("container", name) +func DockerrFilterContainer(nameOrID string) filters.KeyValuePair { + return filters.Arg("container", nameOrID) } func NewDockerWatcher(host string) DockerWatcher {