diff --git a/Dockerfile b/Dockerfile index 7b06288a..5415e705 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.6-alpine as builder +FROM golang:1.22.6-alpine AS builder COPY src /src ENV GOCACHE=/root/.cache/go-build WORKDIR /src diff --git a/README.md b/README.md index f96291d0..7386149e 100755 --- a/README.md +++ b/README.md @@ -80,12 +80,14 @@ autocert: - ... # reverse proxy providers configuration providers: - entry_1: - kind: docker - value: # `FROM_ENV` or full url to docker host - entry_2: - kind: file - value: # relative path of file to `config/` + include: + - providers.yml + - other_file_1.yml + - ... + docker: + local: $DOCKER_HOST + remote-1: tcp://10.0.2.1:2375 + remote-2: ssh://root:1234@10.0.2.2 ``` [🔼Back to top](#table-of-content) diff --git a/config.example.yml b/config.example.yml index 5152dbda..01bab152 100644 --- a/config.example.yml +++ b/config.example.yml @@ -11,22 +11,26 @@ # provider: cloudflare # email: # ACME Email # domains: # a list of domains for cert registration -# - +# - x.y.z # options: -# - auth_token: # your zone API token +# - auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token # 3. other providers, check readme for more providers: - local: - kind: docker + include: + - providers.yml # config/providers.yml + # add some more below if you want + # - file1.yml # config/file_1.yml + # - file2.yml + docker: # for value format, see https://docs.docker.com/reference/cli/dockerd/ - # i.e. ssh://user@10.0.1.1:22, tcp://10.0.2.1:2375 - # use FROM_ENV if you have binded docker socket to /var/run/docker.sock - value: FROM_ENV - providers: - kind: file - value: providers.yml + # $DOCKER_HOST implies unix:///var/run/docker.sock by default + local: $DOCKER_HOST + # add more docker providers if needed + # remote-1: tcp://10.0.2.1:2375 + # remote-2: ssh://root:1234@10.0.2.2 + # Fixed options (optional, non hot-reloadable) # timeout_shutdown: 5 diff --git a/schema/config.schema.json b/schema/config.schema.json index 1b871de6..976c9bc8 100644 --- a/schema/config.schema.json +++ b/schema/config.schema.json @@ -34,12 +34,7 @@ "provider": { "title": "DNS Challenge Provider", "type": "string", - "enum": [ - "local", - "cloudflare", - "clouddns", - "duckdns" - ] + "enum": ["local", "cloudflare", "clouddns", "duckdns"] }, "options": { "title": "Provider specific options", @@ -57,12 +52,7 @@ } }, "then": { - "required": [ - "email", - "domains", - "provider", - "options" - ] + "required": ["email", "domains", "provider", "options"] } }, { @@ -76,9 +66,7 @@ "then": { "properties": { "options": { - "required": [ - "auth_token" - ], + "required": ["auth_token"], "additionalProperties": false, "properties": { "auth_token": { @@ -101,11 +89,7 @@ "then": { "properties": { "options": { - "required": [ - "client_id", - "email", - "password" - ], + "required": ["client_id", "email", "password"], "additionalProperties": false, "properties": { "client_id": { @@ -136,9 +120,7 @@ "then": { "properties": { "options": { - "required": [ - "token" - ], + "required": ["token"], "additionalProperties": false, "properties": { "token": { @@ -155,73 +137,54 @@ "providers": { "title": "Proxy providers configuration", "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "description": "Proxy provider", + "additionalProperties": false, + "properties": { + "include": { + "title": "Proxy providers configuration files", + "description": "relative path to 'config'", + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+\\.(yml|yaml)$", + "patternErrorMessage": "Invalid file name" + } + }, + "docker": { + "title": "Docker provider configuration", + "description": "docker clients (name: address)", "type": "object", - "properties": { - "kind": { - "description": "Proxy provider kind", + "patternProperties": { + "^[a-zA-Z0-9-_]+$": { "type": "string", - "enum": [ - "docker", - "file" + "examples": [ + "unix:///var/run/docker.sock", + "tcp://127.0.0.1:2375", + "ssh://user@host:port" + ], + "oneOf": [ + { + "const": "$DOCKER_HOST", + "description": "Use DOCKER_HOST environment variable" + }, + { + "pattern": "^unix://.+$", + "description": "A Unix socket for local Docker communication." + }, + { + "pattern": "^ssh://.+$", + "description": "An SSH connection to a remote Docker host." + }, + { + "pattern": "^fd://.+$", + "description": "A file descriptor for Docker communication." + }, + { + "pattern": "^tcp://.+$", + "description": "A TCP connection to a remote Docker host." + } ] - }, - "value": { - "type": "string" } - }, - "required": [ - "kind", - "value" - ], - "allOf": [ - { - "if": { - "properties": { - "kind": { - "const": "docker" - } - } - }, - "then": { - "if": { - "properties": { - "value": { - "const": "FROM_ENV" - } - } - }, - "then": { - "properties": { - "value": { - "description": "use docker client from environment" - } - } - }, - "else": { - "properties": { - "value": { - "description": "docker client URL", - "examples": [ - "unix:///var/run/docker.sock", - "tcp://127.0.0.1:2375", - "ssh://user@host:port" - ] - } - } - } - }, - "else": { - "properties": { - "value": { - "description": "file path" - } - } - } - } - ] + } } } }, @@ -236,7 +199,5 @@ } }, "additionalProperties": false, - "required": [ - "providers" - ] -} \ No newline at end of file + "required": ["providers"] +} diff --git a/schema/providers.schema.json b/schema/providers.schema.json index d809e244..87ff679c 100644 --- a/schema/providers.schema.json +++ b/schema/providers.schema.json @@ -37,7 +37,7 @@ ] }, "host": { - "anyOf": [ + "oneOf": [ { "type": "string", "format": "ipv4", @@ -69,9 +69,7 @@ "set_headers": {}, "hide_headers": {} }, - "required": [ - "host" - ], + "required": ["host"], "additionalProperties": false, "allOf": [ { @@ -80,10 +78,7 @@ { "properties": { "scheme": { - "enum": [ - "http", - "https" - ] + "enum": ["http", "https"] } } }, @@ -171,9 +166,7 @@ "not": true } }, - "required": [ - "port" - ] + "required": ["port"] } }, { @@ -198,4 +191,4 @@ } }, "additionalProperties": false -} \ No newline at end of file +} diff --git a/setup-binary.sh b/setup-binary.sh deleted file mode 100644 index 61229007..00000000 --- a/setup-binary.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash -set -e -REPO_URL=https://github.com/yusing/go-proxy -BIN_URL="${REPO_URL}/releases/download/${VERSION}/go-proxy" -SRC_URL="${REPO_URL}/archive/refs/tags/${VERSION}.tar.gz" -APP_ROOT="/opt/go-proxy/${VERSION}" -LOG_FILE="/tmp/go-proxy-setup.log" - -if [ -z "$VERSION" ] || [ "$VERSION" = "latest" ]; then - VERSION_URL="${REPO_URL}/raw/main/version.txt" - VERSION=$(wget -qO- "$VERSION_URL") -fi - -if [ -d "$APP_ROOT" ]; then - echo "$APP_ROOT already exists" - exit 1 -fi - -# check if wget exists -if ! [ -x "$(command -v wget)" ]; then - echo "wget is not installed" - exit 1 -fi - -# check if make exists -if ! [ -x "$(command -v make)" ]; then - echo "make is not installed" - exit 1 -fi - -dl_source() { - cd /tmp - echo "Downloading go-proxy source ${VERSION}" - wget -c "${SRC_URL}" -O go-proxy.tar.gz &> $LOG_FILE - if [ $? -gt 0 ]; then - echo "Source download failed, check your internet connection and version number" - exit 1 - fi - echo "Done" - echo "Extracting go-proxy source ${VERSION}" - tar xzf go-proxy.tar.gz &> $LOG_FILE - if [ $? -gt 0 ]; then - echo "failed to untar go-proxy.tar.gz" - exit 1 - fi - rm go-proxy.tar.gz - mkdir -p "$(dirname "${APP_ROOT}")" - mv "go-proxy-${VERSION}" "$APP_ROOT" - cd "$APP_ROOT" - echo "Done" -} -dl_binary() { - mkdir -p bin - echo "Downloading go-proxy binary ${VERSION}" - wget -c "${BIN_URL}" -O bin/go-proxy &> $LOG_FILE - if [ $? -gt 0 ]; then - echo "Binary download failed, check your internet connection and version number" - exit 1 - fi - chmod +x bin/go-proxy - echo "Done" -} -setup() { - make setup &> $LOG_FILE - if [ $? -gt 0 ]; then - echo "make setup failed" - exit 1 - fi - # SETUP_CODEMIRROR = 1 - if [ "$SETUP_CODEMIRROR" != "0" ]; then - make setup-codemirror &> $LOG_FILE || echo "make setup-codemirror failed, ignored" - fi -} - -dl_source -dl_binary -setup - -# setup systemd - -# check if systemctl exists -if ! command -v systemctl is-system-running > /dev/null 2>&1; then - echo "systemctl not found, skipping systemd setup" - exit 0 -fi -systemctl_failed() { - echo "Failed to enable and start go-proxy" - systemctl status go-proxy - exit 1 -} -echo "Setting up systemd service" -cat < /etc/systemd/system/go-proxy.service -[Unit] -Description=go-proxy reverse proxy -After=network-online.target -Wants=network-online.target systemd-networkd-wait-online.service -[Service] -Type=simple -ExecStart=${APP_ROOT}/bin/go-proxy -WorkingDirectory=${APP_ROOT} -Environment="GOPROXY_IS_SYSTEMD=1" -Restart=on-failure -RestartSec=1s -KillMode=process -KillSignal=SIGINT -TimeoutStartSec=5s -TimeoutStopSec=5s -[Install] -WantedBy=multi-user.target -EOF -systemctl daemon-reload &>$LOG_FILE || systemctl_failed -systemctl enable --now go-proxy &>$LOG_FILE || systemctl_failed -echo "Done" -echo "Setup complete" \ No newline at end of file diff --git a/src/common/constants.go b/src/common/constants.go index afa12587..344502b7 100644 --- a/src/common/constants.go +++ b/src/common/constants.go @@ -35,7 +35,7 @@ const ( ProvidersSchemaPath = SchemaBasePath + "providers.schema.json" ) -const DockerHostFromEnv = "FROM_ENV" +const DockerHostFromEnv = "$DOCKER_HOST" const ( ProxyHTTPPort = ":80" diff --git a/src/config/config.go b/src/config/config.go index defe06d4..3f2c432a 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -134,16 +134,9 @@ func (cfg *Config) Statistics() map[string]interface{} { panic("bug: should not reach here") } }) + stats["type"] = p.GetType() stats["num_streams"] = nStreams stats["num_reverse_proxies"] = nRPs - switch p.ProviderImpl.(type) { - case *PR.DockerProvider: - stats["type"] = "docker" - case *PR.FileProvider: - stats["type"] = "file" - default: - panic("bug: should not reach here") - } providerStats[p.GetName()] = stats }) @@ -202,7 +195,7 @@ func (cfg *Config) load() E.NestedError { } if !common.NoSchemaValidation { - if err := Validate(data); err.IsNotNil() { + if err = Validate(data); err.IsNotNil() { return err } } @@ -220,13 +213,19 @@ func (cfg *Config) load() E.NestedError { cfg.l.Debug("starting providers") cfg.proxyProviders = F.NewMap[string, *PR.Provider]() - for name, pm := range model.Providers { - p := PR.NewProvider(name, pm) - cfg.proxyProviders.Set(name, p) - if err := p.StartAllRoutes(); err.IsNotNil() { - warnings.Add(E.Failure("start routes").Subjectf("provider %s", name).With(err)) - } + for _, filename := range model.Providers.Files { + p := PR.NewFileProvider(filename) + cfg.proxyProviders.Set(p.GetName(), p) } + for name, dockerHost := range model.Providers.Docker { + p := PR.NewDockerProvider(name, dockerHost) + cfg.proxyProviders.Set(p.GetName(), p) + } + cfg.proxyProviders.EachKV(func(name string, p *PR.Provider) { + if err := p.StartAllRoutes(); err.IsNotNil() { + warnings.Add(E.Failure("start routes").Subject(p).With(err)) + } + }) cfg.l.Debug("started providers") cfg.value = model @@ -244,7 +243,7 @@ func (cfg *Config) controlProviders(action string, do func(*PR.Provider) E.Neste cfg.proxyProviders.EachKVParallel(func(name string, p *PR.Provider) { if err := do(p); err.IsNotNil() { - errors.Add(E.From(err).Subjectf("provider %s", name)) + errors.Add(E.From(err).Subject(p)) } }) diff --git a/src/error/error.go b/src/error/error.go index 3164d0dc..0aad026a 100644 --- a/src/error/error.go +++ b/src/error/error.go @@ -20,7 +20,7 @@ type ( // Caller then should handle the nested error, // and continue with the valid values. NestedError struct { - subject any + subject string err error // can be nil extras []NestedError } @@ -96,7 +96,14 @@ func (ne NestedError) Extraf(format string, args ...any) NestedError { } func (ne NestedError) Subject(s any) NestedError { - ne.subject = s + switch ss := s.(type) { + case string: + ne.subject = ss + case fmt.Stringer: + ne.subject = ss.String() + default: + ne.subject = fmt.Sprint(s) + } return ne } @@ -107,7 +114,8 @@ func (ne NestedError) Subjectf(format string, args ...any) NestedError { if strings.Contains(format, "%w") { panic("Subjectf format should not contain %w") } - return ne.Subject(fmt.Sprintf(format, args...)) + ne.subject = fmt.Sprintf(format, args...) + return ne } func (ne NestedError) IsNil() bool { @@ -134,7 +142,7 @@ func (ne *NestedError) writeToSB(sb *strings.Builder, level int, prefix string) if ne.err != nil { sb.WriteString(ne.err.Error()) } - if ne.subject != nil { + if ne.subject != "" { if ne.err != nil { sb.WriteString(fmt.Sprintf(" for %q", ne.subject)) } else { diff --git a/src/go.mod b/src/go.mod index 0215843c..cf787071 100644 --- a/src/go.mod +++ b/src/go.mod @@ -3,8 +3,8 @@ module github.com/yusing/go-proxy go 1.22 require ( - github.com/docker/cli v27.1.1+incompatible - github.com/docker/docker v27.1.1+incompatible + github.com/docker/cli v27.1.2+incompatible + github.com/docker/docker v27.1.2+incompatible github.com/fsnotify/fsnotify v1.7.0 github.com/go-acme/lego/v4 v4.17.4 github.com/santhosh-tekuri/jsonschema v1.2.4 diff --git a/src/go.sum b/src/go.sum index d2e85732..cd58fde0 100644 --- a/src/go.sum +++ b/src/go.sum @@ -13,10 +13,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= -github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v27.1.2+incompatible h1:nYviRv5Y+YAKx3dFrTvS1ErkyVVunKOhoweCTE1BsnI= +github.com/docker/cli v27.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v27.1.2+incompatible h1:AhGzR1xaQIy53qCkxARaFluI00WPGtXn0AJuoQsVYTY= +github.com/docker/docker v27.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= diff --git a/src/models/proxy_entry.go b/src/models/proxy_entry.go index 5eebe2a9..e884a87d 100644 --- a/src/models/proxy_entry.go +++ b/src/models/proxy_entry.go @@ -40,4 +40,10 @@ func (e *ProxyEntry) SetDefaults() { if e.Path == "" { e.Path = "/" } + switch e.Scheme { + case "http": + e.Port = "80" + case "https": + e.Port = "443" + } } diff --git a/src/models/proxy_provider.go b/src/models/proxy_provider.go deleted file mode 100644 index 4149356e..00000000 --- a/src/models/proxy_provider.go +++ /dev/null @@ -1,9 +0,0 @@ -package model - -type ( - ProxyProvider struct { - Kind string `json:"kind"` // docker, file - Value string `json:"value"` - } - ProxyProviders = map[string]ProxyProvider -) diff --git a/src/models/proxy_providers.go b/src/models/proxy_providers.go new file mode 100644 index 00000000..1cca0b6c --- /dev/null +++ b/src/models/proxy_providers.go @@ -0,0 +1,6 @@ +package model + +type ProxyProviders struct { + Files []string `yaml:"include" json:"include"` // docker, file + Docker map[string]string `yaml:"docker" json:"docker"` +} diff --git a/src/proxy/fields/port.go b/src/proxy/fields/port.go index af1808ee..2c9532f6 100644 --- a/src/proxy/fields/port.go +++ b/src/proxy/fields/port.go @@ -11,7 +11,7 @@ type Port int func NewPort(v string) (Port, E.NestedError) { p, err := strconv.Atoi(v) if err != nil { - return ErrPort, E.From(err) + return ErrPort, E.Invalid("port number", v).With(err) } return NewPortInt(p) } diff --git a/src/proxy/provider/docker_provider.go b/src/proxy/provider/docker_provider.go index 861fbea6..2512bc66 100755 --- a/src/proxy/provider/docker_provider.go +++ b/src/proxy/provider/docker_provider.go @@ -16,8 +16,8 @@ type DockerProvider struct { dockerHost string } -func DockerProviderImpl(model *M.ProxyProvider) ProviderImpl { - return &DockerProvider{dockerHost: model.Value} +func DockerProviderImpl(dockerHost string) ProviderImpl { + return &DockerProvider{dockerHost: dockerHost} } // GetProxyEntries returns proxy entries from a docker client. @@ -32,15 +32,16 @@ func DockerProviderImpl(model *M.ProxyProvider) ProviderImpl { // - p: A pointer to the DockerProvider struct. // // Returns: -// - P.EntryModelSlice: A slice of EntryModel structs representing the proxy entries. +// - P.EntryModelSlice: (non-nil) A slice of EntryModel structs representing the proxy entries. // - error: An error object if there was an error retrieving the docker client information or parsing the labels. func (p DockerProvider) GetProxyEntries() (M.ProxyEntries, E.NestedError) { + entries := M.NewProxyEntries() + info, err := D.GetClientInfo(p.dockerHost) if err.IsNotNil() { - return nil, E.From(err) + return entries, E.From(err) } - entries := M.NewProxyEntries() errors := E.NewBuilder("errors when parse docker labels for %q", p.dockerHost) for _, container := range info.Containers { diff --git a/src/proxy/provider/file_provider.go b/src/proxy/provider/file_provider.go index f0bfc942..0b3f289b 100644 --- a/src/proxy/provider/file_provider.go +++ b/src/proxy/provider/file_provider.go @@ -16,10 +16,10 @@ type FileProvider struct { path string } -func FileProviderImpl(m *M.ProxyProvider) ProviderImpl { +func FileProviderImpl(filename string) ProviderImpl { return &FileProvider{ - fileName: m.Value, - path: path.Join(common.ConfigBasePath, m.Value), + fileName: filename, + path: path.Join(common.ConfigBasePath, filename), } } @@ -27,13 +27,17 @@ func Validate(data []byte) E.NestedError { return U.ValidateYaml(U.GetSchema(common.ProvidersSchemaPath), data) } +func (p *FileProvider) String() string { + return p.fileName +} + func (p *FileProvider) GetProxyEntries() (M.ProxyEntries, E.NestedError) { entries := M.NewProxyEntries() data, err := E.Check(os.ReadFile(p.path)) if err.IsNotNil() { - return entries, E.Failure("read file").Subject(p.fileName).With(err) + return entries, E.Failure("read file").Subject(p).With(err) } - ne := E.Failure("validation").Subject(p.fileName) + ne := E.Failure("validation").Subject(p) if !common.NoSchemaValidation { if err = Validate(data); err.IsNotNil() { return entries, ne.With(err) diff --git a/src/proxy/provider/provider.go b/src/proxy/provider/provider.go index a8184439..298914a0 100644 --- a/src/proxy/provider/provider.go +++ b/src/proxy/provider/provider.go @@ -2,9 +2,10 @@ package provider import ( "context" + "fmt" + "path" "github.com/sirupsen/logrus" - "github.com/yusing/go-proxy/common" E "github.com/yusing/go-proxy/error" M "github.com/yusing/go-proxy/models" R "github.com/yusing/go-proxy/route" @@ -20,6 +21,7 @@ type Provider struct { ProviderImpl name string + t ProviderType routes *R.Routes reloadReqCh chan struct{} @@ -30,27 +32,49 @@ type Provider struct { l *logrus.Entry } -func NewProvider(name string, model M.ProxyProvider) (p *Provider) { - p = &Provider{ +type ProviderType string + +const ( + ProviderTypeDocker ProviderType = "docker" + ProviderTypeFile ProviderType = "file" +) + +func newProvider(name string, t ProviderType) *Provider { + return &Provider{ name: name, + t: t, routes: R.NewRoutes(), reloadReqCh: make(chan struct{}, 1), l: logrus.WithField("provider", name), } - switch model.Kind { - case common.ProviderKind_Docker: - p.ProviderImpl = DockerProviderImpl(&model) - case common.ProviderKind_File: - p.ProviderImpl = FileProviderImpl(&model) - } +} +func NewFileProvider(filename string) *Provider { + name := path.Base(filename) + p := newProvider(name, ProviderTypeFile) + p.ProviderImpl = FileProviderImpl(filename) p.watcher = p.NewWatcher() - return + return p +} + +func NewDockerProvider(name string, dockerHost string) *Provider { + p := newProvider(name, ProviderTypeDocker) + p.ProviderImpl = DockerProviderImpl(dockerHost) + p.watcher = p.NewWatcher() + return p } func (p *Provider) GetName() string { return p.name } +func (p *Provider) GetType() ProviderType { + return p.t +} + +func (p *Provider) String() string { + return fmt.Sprintf("%s (%s provider)", p.name, p.t) +} + func (p *Provider) StartAllRoutes() E.NestedError { err := p.loadRoutes() @@ -58,23 +82,24 @@ func (p *Provider) StartAllRoutes() E.NestedError { p.watcherCtx, p.watcherCancel = context.WithCancel(context.Background()) go p.watchEvents() - if err.IsNotNil() { - return err - } - errors := E.NewBuilder("errors starting routes for provider %q", p.name) + errors := E.NewBuilder("errors in routes") nStarted := 0 + nFailed := 0 + + if err.IsNotNil() { + errors.Add(err) + } + p.routes.EachKVParallel(func(alias string, r R.Route) { if err := r.Start(); err.IsNotNil() { - errors.Add(err.Subject(alias)) + errors.Add(err.Subject(r)) + nFailed++ } else { nStarted++ } }) - if err := errors.Build(); err.IsNotNil() { - return err - } - p.l.Infof("%d routes started", nStarted) - return E.Nil() + p.l.Infof("%d routes started, %d failed", nStarted, nFailed) + return errors.Build() } func (p *Provider) StopAllRoutes() E.NestedError { @@ -87,7 +112,7 @@ func (p *Provider) StopAllRoutes() E.NestedError { nStopped := 0 p.routes.EachKVParallel(func(alias string, r R.Route) { if err := r.Stop(); err.IsNotNil() { - errors.Add(err.Subject(alias)) + errors.Add(err.Subject(r)) } else { nStopped++ } @@ -146,7 +171,7 @@ func (p *Provider) loadRoutes() E.NestedError { entries, err := p.GetProxyEntries() if err.IsNotNil() { - p.l.Warn(err.Subjectf("provider %s", p.name)) + p.l.Warn(err.Subject(p)) } p.routes = R.NewRoutes() diff --git a/src/route/http_route.go b/src/route/http_route.go index 5c2247e3..8c35e25a 100755 --- a/src/route/http_route.go +++ b/src/route/http_route.go @@ -106,6 +106,10 @@ func NewHTTPRoute(entry *P.Entry) (*HTTPRoute, E.NestedError) { return r, E.Nil() } +func (r *HTTPRoute) String() string { + return fmt.Sprintf("%s (reverse proxy)", r.Alias) +} + func (r *HTTPRoute) Start() E.NestedError { httpRoutes.Set(r.Alias.String(), r) return E.Nil() diff --git a/src/route/route.go b/src/route/route.go index 5efa9275..bf448aeb 100755 --- a/src/route/route.go +++ b/src/route/route.go @@ -11,6 +11,7 @@ type ( Route interface { Start() E.NestedError Stop() E.NestedError + String() string } Routes = F.Map[string, Route] ) diff --git a/src/route/stream_route.go b/src/route/stream_route.go index a7ea93f9..1ac2154c 100755 --- a/src/route/stream_route.go +++ b/src/route/stream_route.go @@ -49,6 +49,10 @@ func NewStreamRoute(entry *P.StreamEntry) (*StreamRoute, E.NestedError) { return base, E.Nil() } +func (r *StreamRoute) String() string { + return fmt.Sprintf("%s (%v stream)", r.Alias, r.Scheme) +} + func (r *StreamRoute) Start() E.NestedError { if r.started.Load() { return E.Invalid("state", "already started")