mirror of
https://github.com/kemko/reproxy.git
synced 2026-01-01 15:55:49 +03:00
108 lines
2.4 KiB
Go
108 lines
2.4 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"regexp"
|
|
"sort"
|
|
"time"
|
|
|
|
log "github.com/go-pkgz/lgr"
|
|
"github.com/pkg/errors"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/umputun/reproxy/app/discovery"
|
|
)
|
|
|
|
// File implements file-based provider, defined with yaml file
|
|
type File struct {
|
|
FileName string
|
|
CheckInterval time.Duration
|
|
Delay time.Duration
|
|
}
|
|
|
|
// Events returns channel updating on file change only
|
|
func (d *File) Events(ctx context.Context) <-chan struct{} {
|
|
res := make(chan struct{})
|
|
|
|
// no need to queue multiple events
|
|
trySubmit := func(ch chan struct{}) {
|
|
select {
|
|
case ch <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
tk := time.NewTicker(d.CheckInterval)
|
|
lastModif := time.Time{}
|
|
for {
|
|
select {
|
|
case <-tk.C:
|
|
fi, err := os.Stat(d.FileName)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if fi.ModTime() != lastModif {
|
|
// don't react on modification right away
|
|
if fi.ModTime().Sub(lastModif) < d.Delay {
|
|
continue
|
|
}
|
|
log.Printf("[DEBUG] file %s changed, %s -> %s", d.FileName,
|
|
lastModif.Format(time.RFC3339Nano), fi.ModTime().Format(time.RFC3339Nano))
|
|
lastModif = fi.ModTime()
|
|
trySubmit(res)
|
|
}
|
|
case <-ctx.Done():
|
|
close(res)
|
|
tk.Stop()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return res
|
|
}
|
|
|
|
// List all src dst pairs
|
|
func (d *File) List() (res []discovery.UrlMapper, err error) {
|
|
|
|
var fileConf map[string][]struct {
|
|
SourceRoute string `yaml:"route"`
|
|
Dest string `yaml:"dest"`
|
|
Ping string `yaml:"ping"`
|
|
}
|
|
fh, err := os.Open(d.FileName)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "can't open %s", d.FileName)
|
|
}
|
|
defer fh.Close()
|
|
|
|
if err = yaml.NewDecoder(fh).Decode(&fileConf); err != nil {
|
|
return nil, errors.Wrapf(err, "can't parse %s", d.FileName)
|
|
}
|
|
log.Printf("[DEBUG] file provider %+v", res)
|
|
|
|
for srv, fl := range fileConf {
|
|
for _, f := range fl {
|
|
rx, err := regexp.Compile(f.SourceRoute)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "can't parse regex %s", f.SourceRoute)
|
|
}
|
|
if srv == "default" {
|
|
srv = "*"
|
|
}
|
|
mapper := discovery.UrlMapper{Server: srv, SrcMatch: *rx, Dst: f.Dest, PingURL: f.Ping}
|
|
res = append(res, mapper)
|
|
}
|
|
}
|
|
sort.Slice(res, func(i, j int) bool {
|
|
if res[i].Server == res[j].Server {
|
|
return res[i].SrcMatch.String() < res[j].SrcMatch.String()
|
|
}
|
|
return res[i].Server < res[j].Server
|
|
})
|
|
return res, nil
|
|
}
|
|
|
|
func (d *File) ID() discovery.ProviderID { return discovery.PIFile }
|