v0.5: (BREAKING) replacing path with path_patterns, improved docker monitoring mechanism, bug fixes

This commit is contained in:
yusing 2024-09-16 13:05:04 +08:00
parent 2e7ba51521
commit 7a0478164f
20 changed files with 252 additions and 246 deletions

View file

@ -1,12 +1,13 @@
{
"yaml.schemas": {
"https://github.com/yusing/go-proxy/raw/main/schema/config.schema.json": [
"config.example.yml",
"config.yml"
],
"https://github.com/yusing/go-proxy/raw/main/schema/providers.schema.json": [
"providers.example.yml",
"*.providers.yml"
]
}
}
"yaml.schemas": {
"https://github.com/yusing/go-proxy/raw/main/schema/config.schema.json": [
"config.example.yml",
"config.yml"
],
"https://github.com/yusing/go-proxy/raw/main/schema/providers.schema.json": [
"providers.example.yml",
"*.providers.yml",
"providers.yml"
]
}
}

View file

@ -13,9 +13,9 @@ FROM alpine:latest
LABEL maintainer="yusing@6uo.me"
RUN apk add --no-cache tzdata
COPY schema/ /app/schema
# copy binary
COPY --from=builder /src/go-proxy /app/
COPY schema/ /app/schema
RUN chmod +x /app/go-proxy
ENV DOCKER_HOST unix:///var/run/docker.sock

View file

@ -77,7 +77,7 @@
6. Run `docker compose up -d` to start the container
7. Navigate to Web panel `http://gp.yourdomain.com` and edit proxy config
7. Navigate to Web panel `http://gp.yourdomain.com` or use **Visual Studio Code (provides schema check)** to edit proxy config
[🔼Back to top](#table-of-content)
@ -93,17 +93,16 @@
### Fields
| Field | Description | Default | Allowed Values / Syntax |
| --------------------- | ---------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scheme` | proxy protocol | <ul><li>`http` for numeric port</li><li>`tcp` for `x:y` port</li></ul> | `http`, `https`, `tcp`, `udp` |
| `host` | proxy host | `container_name` | IP address, hostname |
| `port` | proxy port **(http/s)** | first port in `ports:` | number in range of `0 - 65535` |
| `port` **(required)** | proxy port **(tcp/udp)** | N/A | `x:y` <br><ul><li>x: port for `go-proxy` to listen on</li><li>y: port or [_service name_](../src/common/constants.go#L55) of target container</li></ul> |
| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
| `path` | proxy path | empty | **(http/s only)** string |
| `path_mode` | path handling **(http/s only)** | empty | empty, `forward` |
| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[<sup>1</sup>](#1-key-value-mapping-example) |
| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[<sup>2</sup>](#2-list-example) |
| Field | Description | Default | Allowed Values / Syntax |
| --------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scheme` | proxy protocol | <ul><li>`http` for numeric port</li><li>`tcp` for `x:y` port</li></ul> | `http`, `https`, `tcp`, `udp` |
| `host` | proxy host | <ul><li>Docker: `container_name`</li><li>File: `localhost`</li></ul> | IP address, hostname |
| `port` | proxy port **(http/s)** | first port in `ports:` | number in range of `1 - 65535` |
| `port` **(required)** | proxy port **(tcp/udp)** | N/A | `x:y` <br><ul><li>x: port for `go-proxy` to listen on</li><li>y: port or [_service name_](../src/common/constants.go#L55) of target container</li></ul> |
| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
| `path_patterns` | proxy path patterns **(http/s only)**<br> only requests that matched a pattern will be proxied | empty **(proxy all requests)** | yaml style list[<sup>1</sup>](#list-example) of path patterns ([syntax](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[<sup>2</sup>](#key-value-mapping-example) of header-value pairs |
| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[<sup>1</sup>](#list-example) of headers |
#### Key-value mapping example
@ -142,6 +141,9 @@ services:
nginx:
...
labels:
proxy.nginx.path_patterns: | # remember to add the '|'
- GET /
- POST /auth
proxy.nginx.hide_headers: | # remember to add the '|'
- X-Custom-Header1
- X-Custom-Header2
@ -152,6 +154,9 @@ File Provider
```yaml
service_a:
host: service_a.internal
path_patterns:
- GET /
- POST /auth
hide_headers:
- X-Custom-Header1
- X-Custom-Header2

View file

@ -23,16 +23,19 @@
},
"cert_path": {
"title": "path of cert file to load/store",
"description": "default: certs/cert.crt",
"default": "certs/cert.crt",
"markdownDescription": "default: `certs/cert.crt`",
"type": "string"
},
"key_path": {
"title": "path of key file to load/store",
"description": "default: certs/priv.key",
"default": "certs/priv.key",
"markdownDescription": "default: `certs/priv.key`",
"type": "string"
},
"provider": {
"title": "DNS Challenge Provider",
"default": "local",
"type": "string",
"enum": ["local", "cloudflare", "clouddns", "duckdns"]
},
@ -44,10 +47,11 @@
"allOf": [
{
"if": {
"properties": {
"provider": {
"not": true,
"const": "local"
"not": {
"properties": {
"provider": {
"const": "local"
}
}
}
},
@ -151,7 +155,7 @@
},
"docker": {
"title": "Docker provider configuration",
"description": "docker clients (name: address)",
"description": "docker clients (name-address pairs)",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9-_]+$": {
@ -194,7 +198,7 @@
"minimum": 0
},
"redirect_to_https": {
"title": "Redirect to HTTPS",
"title": "Redirect to HTTPS on HTTP requests",
"type": "boolean"
}
},

View file

@ -32,12 +32,17 @@
},
{
"type": "null",
"description": "Auto detect base on port number"
"description": "Auto detect base on port format"
}
]
},
"host": {
"default": "localhost",
"oneOf": [
{
"type": "null",
"description": "localhost (default)"
},
{
"type": "string",
"format": "ipv4",
@ -56,59 +61,38 @@
],
"title": "Proxy host (ipv4 / ipv6 / hostname)"
},
"port": {
"title": "Proxy port"
},
"path": {
"title": "Proxy path pattern (See https://pkg.go.dev/net/http#ServeMux)"
},
"no_tls_verify": {
"description": "Disable TLS verification for https proxy",
"type": "boolean"
},
"port": {},
"no_tls_verify": {},
"path_patterns": {},
"set_headers": {},
"hide_headers": {}
},
"required": ["host"],
"additionalProperties": false,
"allOf": [
{
"if": {
"anyOf": [
{
"properties": {
"scheme": {
"properties": {
"scheme": {
"anyOf": [
{
"enum": ["http", "https"]
}
}
},
{
"properties": {
"scheme": {
"not": true
}
}
},
{
"properties": {
"scheme": {
},
{
"type": "null"
}
}
]
}
]
}
},
"then": {
"properties": {
"port": {
"markdownDescription": "Proxy port from **1** to **65535**",
"oneOf": [
{
"type": "string",
"pattern": "^[0-9]{1,5}$",
"minimum": 1,
"maximum": 65535,
"markdownDescription": "Proxy port from **1** to **65535**",
"patternErrorMessage": "'port' must be a number"
"pattern": "^\\d{1,5}$",
"patternErrorMessage": "`port` must be a number"
},
{
"type": "integer",
@ -117,11 +101,16 @@
}
]
},
"path": {
"path_patterns": {
"oneOf": [
{
"type": "string",
"description": "Proxy path"
"type": "array",
"markdownDescription": "A list of [path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)",
"items": {
"type": "string",
"pattern": "^((GET|POST|DELETE|PUT|PATCH|HEAD|OPTIONS|CONNECT)\\s)?(/\\w*)+/?$",
"patternErrorMessage": "invalid path pattern"
}
},
{
"type": "null",
@ -133,7 +122,6 @@
"type": "object",
"description": "Proxy headers to set",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
@ -151,12 +139,15 @@
"else": {
"properties": {
"port": {
"markdownDescription": "`listening port`:`proxy port | service name`",
"markdownDescription": "`listening port:proxy port` or `listening port:service name`",
"type": "string",
"pattern": "^[0-9]+\\:[0-9a-z]+$",
"patternErrorMessage": "'port' must be in the format of '<listening port>:<proxy port | service name>'"
"patternErrorMessage": "invalid syntax"
},
"path": {
"no_tls_verify": {
"not": true
},
"path_patterns": {
"not": true
},
"set_headers": {
@ -171,15 +162,22 @@
},
{
"if": {
"not": {
"properties": {
"scheme": {
"const": "https"
}
"properties": {
"scheme": {
"const": "https"
}
}
},
"then": {
"properties": {
"no_tls_verify": {
"description": "Disable TLS verification for https proxy",
"type": "boolean",
"default": false
}
}
},
"else": {
"properties": {
"no_tls_verify": {
"not": true

View file

@ -6,7 +6,6 @@ import (
U "github.com/yusing/go-proxy/api/v1/utils"
"github.com/yusing/go-proxy/config"
PT "github.com/yusing/go-proxy/proxy/fields"
R "github.com/yusing/go-proxy/route"
)
@ -24,17 +23,7 @@ func CheckHealth(cfg *config.Config, w http.ResponseWriter, r *http.Request) {
U.HandleErr(w, r, U.ErrNotFound("target", target), http.StatusNotFound)
return
case *R.HTTPRoute:
path, err := PT.NewPath(r.FormValue("path"))
if err.IsNotNil() {
U.HandleErr(w, r, err, http.StatusBadRequest)
return
}
sr, hasSr := route.GetSubroute(path)
if !hasSr {
U.HandleErr(w, r, U.ErrNotFound("path", string(path)), http.StatusNotFound)
return
}
ok = U.IsSiteHealthy(sr.TargetURL.String())
ok = U.IsSiteHealthy(route.TargetURL.String())
case *R.StreamRoute:
ok = U.IsStreamHealthy(
string(route.Scheme.ProxyScheme),

View file

@ -1,32 +1,37 @@
package docker
import (
"net/http"
"strings"
E "github.com/yusing/go-proxy/error"
"gopkg.in/yaml.v3"
)
func yamlParser[T any](value string) (any, E.NestedError) {
var data T
func yamlListParser(value string) (any, E.NestedError) {
value = strings.TrimSpace(value)
if value == "" {
return []string{}, E.Nil()
}
var data []string
err := E.From(yaml.Unmarshal([]byte(value), &data))
return data, err
}
func setHeadersParser(value string) (any, E.NestedError) {
func yamlStringMappingParser(value string) (any, E.NestedError) {
value = strings.TrimSpace(value)
lines := strings.Split(value, "\n")
h := make(http.Header)
h := make(map[string]string)
for _, line := range lines {
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
return nil, E.Invalid("set header statement", line)
}
key := strings.TrimSpace(parts[0])
vals := strings.Split(parts[1], ",")
for i := range vals {
h.Add(key, strings.TrimSpace(vals[i]))
val := strings.TrimSpace(parts[1])
if existing, ok := h[key]; ok {
h[key] = existing + ", " + val
} else {
h[key] = val
}
}
return h, E.Nil()
@ -56,8 +61,9 @@ const NSProxy = "proxy"
var _ = func() int {
RegisterNamespace(NSProxy, ValueParserMap{
"aliases": commaSepParser,
"set_headers": setHeadersParser,
"hide_headers": yamlParser[[]string],
"path_patterns": yamlListParser,
"set_headers": yamlStringMappingParser,
"hide_headers": yamlListParser,
"no_tls_verify": boolParser,
})
return 0

View file

@ -2,7 +2,6 @@ package docker
import (
"fmt"
"net/http"
"reflect"
"strings"
"testing"
@ -82,26 +81,22 @@ X-Custom-Header1: foo, bar
X-Custom-Header1: baz
X-Custom-Header2: boo`
v = strings.TrimPrefix(v, "\n")
h := make(http.Header, 0)
h.Set("X-Custom-Header1", "foo")
h.Add("X-Custom-Header1", "bar")
h.Add("X-Custom-Header1", "baz")
h.Set("X-Custom-Header2", "boo")
h := map[string]string{
"X-Custom-Header1": "foo, bar, baz",
"X-Custom-Header2": "boo",
}
pl, err := ParseLabel(makeLabel(NSProxy, "foo", "set_headers"), v)
if err.IsNotNil() {
t.Errorf("expected err=nil, got %s", err.Error())
}
hGot, ok := pl.Value.(http.Header)
hGot, ok := pl.Value.(map[string]string)
if !ok {
t.Error("value is not http.Header")
t.Errorf("value is not a map[string]string, but %T", pl.Value)
return
}
for k, vWant := range h {
vGot := hGot[k]
if !reflect.DeepEqual(vGot, vWant) {
t.Errorf("expected %s=%q, got %q", k, vWant, vGot)
}
if !reflect.DeepEqual(h, hGot) {
t.Errorf("expected %v, got %v", h, hGot)
}
}

View file

@ -20,7 +20,6 @@ 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() {
@ -70,7 +69,6 @@ func main() {
onShutdown.Add(func() {
docker.CloseAllClients()
W.StopAllFileWatchers()
cfg.Dispose()
})

View file

@ -1,7 +1,6 @@
package model
import (
"net/http"
"strings"
F "github.com/yusing/go-proxy/utils/functional"
@ -9,14 +8,14 @@ import (
type (
ProxyEntry struct {
Alias string `yaml:"-" json:"-"`
Scheme string `yaml:"scheme" json:"scheme"`
Host string `yaml:"host" json:"host"`
Port string `yaml:"port" json:"port"`
NoTLSVerify bool `yaml:"no_tls_verify" json:"no_tls_verify"` // http proxy only
Path string `yaml:"path" json:"path"` // http proxy only
SetHeaders http.Header `yaml:"set_headers" json:"set_headers"` // http proxy only
HideHeaders []string `yaml:"hide_headers" json:"hide_headers"` // http proxy only
Alias string `yaml:"-" json:"-"`
Scheme string `yaml:"scheme" json:"scheme"`
Host string `yaml:"host" json:"host"`
Port string `yaml:"port" json:"port"`
NoTLSVerify bool `yaml:"no_tls_verify" json:"no_tls_verify"` // https proxy only
PathPatterns []string `yaml:"path_patterns" json:"path_patterns"` // http(s) proxy only
SetHeaders map[string]string `yaml:"set_headers" json:"set_headers"` // http(s) proxy only
HideHeaders []string `yaml:"hide_headers" json:"hide_headers"` // http(s) proxy only
}
ProxyEntries = *F.Map[string, *ProxyEntry]
@ -37,8 +36,8 @@ func (e *ProxyEntry) SetDefaults() {
}
}
}
if e.Path == "" {
e.Path = "/"
if e.Host == "" {
e.Host = "localhost"
}
switch e.Scheme {
case "http":

View file

@ -12,15 +12,15 @@ import (
type (
Entry struct { // real model after validation
Alias T.Alias
Scheme T.Scheme
Host T.Host
Port T.Port
URL *url.URL
NoTLSVerify bool
Path T.Path
SetHeaders http.Header
HideHeaders []string
Alias T.Alias
Scheme T.Scheme
Host T.Host
Port T.Port
URL *url.URL
NoTLSVerify bool
PathPatterns T.PathPatterns
SetHeaders http.Header
HideHeaders []string
}
StreamEntry struct {
Alias T.Alias `json:"alias"`
@ -51,7 +51,11 @@ func validateEntry(m *M.ProxyEntry, s T.Scheme) (*Entry, E.NestedError) {
if err.IsNotNil() {
return nil, err
}
path, err := T.NewPath(m.Path)
pathPatterns, err := T.NewPathPatterns(m.PathPatterns)
if err.IsNotNil() {
return nil, err
}
setHeaders, err := T.NewHTTPHeaders(m.SetHeaders)
if err.IsNotNil() {
return nil, err
}
@ -60,15 +64,15 @@ func validateEntry(m *M.ProxyEntry, s T.Scheme) (*Entry, E.NestedError) {
return nil, err
}
return &Entry{
Alias: T.NewAlias(m.Alias),
Scheme: s,
Host: host,
Port: port,
URL: url,
NoTLSVerify: m.NoTLSVerify,
Path: path,
SetHeaders: m.SetHeaders,
HideHeaders: m.HideHeaders,
Alias: T.NewAlias(m.Alias),
Scheme: s,
Host: host,
Port: port,
URL: url,
NoTLSVerify: m.NoTLSVerify,
PathPatterns: pathPatterns,
SetHeaders: setHeaders,
HideHeaders: m.HideHeaders,
}, E.Nil()
}

View file

@ -0,0 +1,19 @@
package fields
import (
"net/http"
"strings"
E "github.com/yusing/go-proxy/error"
)
func NewHTTPHeaders(headers map[string]string) (http.Header, E.NestedError) {
h := make(http.Header)
for k, v := range headers {
vSplit := strings.Split(v, ",")
for _, header := range vSplit {
h.Add(k, strings.TrimSpace(header))
}
}
return h, E.Nil()
}

View file

@ -1,14 +0,0 @@
package fields
import (
E "github.com/yusing/go-proxy/error"
)
type Path string
func NewPath(s string) (Path, E.NestedError) {
if s == "" || s[0] == '/' {
return Path(s), E.Nil()
}
return "", E.Invalid("path", s).With("must be empty or start with '/'")
}

View file

@ -0,0 +1,37 @@
package fields
import (
"regexp"
E "github.com/yusing/go-proxy/error"
)
type PathPattern string
type PathPatterns = []PathPattern
func NewPathPattern(s string) (PathPattern, E.NestedError) {
if len(s) == 0 {
return "", E.Invalid("path", "must not be empty")
}
if !pathPattern.MatchString(string(s)) {
return "", E.Invalid("path pattern", s)
}
return PathPattern(s), E.Nil()
}
func NewPathPatterns(s []string) (PathPatterns, E.NestedError) {
if len(s) == 0 {
return []PathPattern{"/"}, E.Nil()
}
pp := make(PathPatterns, len(s))
for i, v := range s {
if pattern, err := NewPathPattern(v); err.IsNotNil() {
return nil, err
} else {
pp[i] = pattern
}
}
return pp, E.Nil()
}
var pathPattern = regexp.MustCompile("^((GET|POST|DELETE|PUT|PATCH|HEAD|OPTIONS|CONNECT)\\s)?(/\\w*)+/?$")

View file

@ -107,6 +107,7 @@ func (p *Provider) StartAllRoutes() E.NestedError {
func (p *Provider) StopAllRoutes() E.NestedError {
if p.watcherCancel != nil {
p.watcherCancel()
p.watcherCancel = nil
}
errors := E.NewBuilder("errors stopping routes for provider %q", p.name)
nStopped := 0
@ -126,17 +127,9 @@ func (p *Provider) StopAllRoutes() E.NestedError {
func (p *Provider) ReloadRoutes() {
defer p.l.Info("routes reloaded")
select {
case p.reloadReqCh <- struct{}{}:
defer func() {
<-p.reloadReqCh
}()
p.StopAllRoutes()
p.loadRoutes()
p.StartAllRoutes()
default:
return
}
p.StopAllRoutes()
p.loadRoutes()
p.StartAllRoutes()
}
func (p *Provider) GetCurrentRoutes() *R.Routes {
@ -149,13 +142,14 @@ func (p *Provider) watchEvents() {
for {
select {
case <-p.reloadReqCh:
case <-p.reloadReqCh: // block until last reload is done
p.ReloadRoutes()
continue // ignore events once after reload
case event, ok := <-events:
if !ok {
return
}
l.Infof("watcher event: %s", event)
l.Info(event)
p.reloadReqCh <- struct{}{}
case err, ok := <-errs:
if !ok {

View file

@ -19,23 +19,18 @@ import (
type (
HTTPRoute struct {
Alias PT.Alias `json:"alias"`
Subroutes HTTPSubroutes `json:"subroutes"`
Alias PT.Alias `json:"alias"`
mux *http.ServeMux
TargetURL URL
PathPatterns PT.PathPatterns
mux *http.ServeMux
handler *P.ReverseProxy
}
HTTPSubroute struct {
TargetURL *URL `json:"targetURL"`
Path PathKey `json:"path"`
proxy *P.ReverseProxy
}
URL url.URL
PathKey = PT.Path
SubdomainKey = PT.Alias
HTTPSubroutes = map[PathKey]HTTPSubroute
URL url.URL
PathKey = PT.PathPattern
SubdomainKey = PT.Alias
)
var httpRoutes = F.NewMap[SubdomainKey, *HTTPRoute]()
@ -57,49 +52,28 @@ func NewHTTPRoute(entry *P.Entry) (*HTTPRoute, E.NestedError) {
r, ok := httpRoutes.UnsafeGet(entry.Alias)
if !ok {
r = &HTTPRoute{
Alias: entry.Alias,
Subroutes: make(HTTPSubroutes),
mux: http.NewServeMux(),
Alias: entry.Alias,
TargetURL: URL(*entry.URL),
PathPatterns: entry.PathPatterns,
handler: rp,
}
httpRoutes.UnsafeSet(entry.Alias, r)
}
path := entry.Path
if _, exists := r.Subroutes[path]; exists {
return nil, E.Duplicated("path", path)
}
r.mux.HandleFunc(string(path), rp.ServeHTTP)
if err := recover(); err != nil {
switch t := err.(type) {
case error:
// NOTE: likely path pattern error
return nil, E.From(t)
default:
return nil, E.From(fmt.Errorf("%v", t))
}
}
sr := HTTPSubroute{
TargetURL: (*URL)(entry.URL),
proxy: rp,
Path: path,
}
rewrite := rp.Rewrite
if logrus.GetLevel() == logrus.DebugLevel {
l := logrus.WithField("alias", entry.Alias)
sr.proxy.Rewrite = func(pr *P.ProxyRequest) {
rp.Rewrite = func(pr *P.ProxyRequest) {
l.Debug("request URL: ", pr.In.Host, pr.In.URL.Path)
l.Debug("request headers: ", pr.In.Header)
rewrite(pr)
}
} else {
sr.proxy.Rewrite = rewrite
rp.Rewrite = rewrite
}
r.Subroutes[path] = sr
return r, E.Nil()
}
@ -108,20 +82,20 @@ func (r *HTTPRoute) String() string {
}
func (r *HTTPRoute) Start() E.NestedError {
r.mux = http.NewServeMux()
for _, p := range r.PathPatterns {
r.mux.HandleFunc(string(p), r.handler.ServeHTTP)
}
httpRoutes.Set(r.Alias, r)
return E.Nil()
}
func (r *HTTPRoute) Stop() E.NestedError {
r.mux = nil
httpRoutes.Delete(r.Alias)
return E.Nil()
}
func (r *HTTPRoute) GetSubroute(path PathKey) (HTTPSubroute, bool) {
sr, ok := r.Subroutes[path]
return sr, ok
}
func (u *URL) String() string {
return (*url.URL)(u).String()
}

View file

@ -2,6 +2,7 @@ package watcher
import (
"context"
"fmt"
"time"
"github.com/docker/docker/api/types/events"
@ -47,14 +48,28 @@ func (w *DockerWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.Nest
for {
select {
case <-ctx.Done():
errCh <- E.From(<-cErrCh)
if err := <-cErrCh; err != nil {
errCh <- E.From(err)
}
return
case msg := <-cEventCh:
var Action Action
switch msg.Action {
case events.ActionStart:
Action = ActionCreated
case events.ActionDie:
Action = ActionDeleted
default: // NOTE: should not happen
Action = ActionModified
}
eventCh <- Event{
ActorName: msg.Actor.Attributes["name"],
Action: ActionModified,
ActorName: fmt.Sprintf("container %q", msg.Actor.Attributes["name"]),
Action: Action,
}
case err := <-cErrCh:
if err == nil {
continue
}
errCh <- E.From(err)
select {
case <-ctx.Done():
@ -74,7 +89,7 @@ func (w *DockerWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.Nest
}
var dwOptions = events.ListOptions{Filters: filters.NewArgs(
filters.Arg("type", "container"),
filters.Arg("event", "start"),
filters.Arg("event", "die"), // 'stop' already triggering 'die'
filters.Arg("type", string(events.ContainerEventType)),
filters.Arg("event", string(events.ActionStart)),
filters.Arg("event", string(events.ActionDie)), // 'stop' already triggering 'die'
)}

View file

@ -12,8 +12,9 @@ type (
const (
ActionModified Action = "MODIFIED"
ActionDeleted Action = "DELETED"
ActionCreated Action = "CREATED"
ActionStarted Action = "STARTED"
ActionDeleted Action = "DELETED"
)
func (e Event) String() string {
@ -23,11 +24,3 @@ func (e Event) String() string {
func (a Action) IsDelete() bool {
return a == ActionDeleted
}
func (a Action) IsModify() bool {
return a == ActionModified
}
func (a Action) IsCreate() bool {
return a == ActionCreated
}

View file

@ -18,10 +18,6 @@ func NewFileWatcher(filename string) Watcher {
return &fileWatcher{filename: filename}
}
func StopAllFileWatchers() {
fwHelper.close()
}
func (f *fileWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.NestedError) {
return fwHelper.Add(ctx, f)
}

View file

@ -80,13 +80,6 @@ func (h *fileWatcherHelper) Remove(w *fileWatcher) {
delete(h.m, w.filename)
}
// deinit closes the fs watcher
// and waits for the start() loop to finish
func (h *fileWatcherHelper) close() {
_ = h.w.Close()
h.wg.Wait() // wait for `start()` loop to finish
}
func (h *fileWatcherHelper) start() {
defer h.wg.Done()