mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-17 05:44:57 +00:00
feat(osiris): reload config upon SIGHUP
Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCantBindToPort = errors.New("bind: can't bind to host:port")
|
ErrInvalidHostpost = errors.New("bind: invalid host:port")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bind struct {
|
type Bind struct {
|
||||||
@@ -19,25 +19,16 @@ type Bind struct {
|
|||||||
func (b *Bind) Valid() error {
|
func (b *Bind) Valid() error {
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", b.HTTP)
|
if _, _, err := net.SplitHostPort(b.HTTP); err != nil {
|
||||||
if err != nil {
|
errs = append(errs, fmt.Errorf("%w %q: %w", ErrInvalidHostpost, b.HTTP, err))
|
||||||
errs = append(errs, fmt.Errorf("%w %q: %w", ErrCantBindToPort, b.HTTP, err))
|
|
||||||
} else {
|
|
||||||
defer ln.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ln, err = net.Listen("tcp", b.HTTPS)
|
if _, _, err := net.SplitHostPort(b.HTTPS); err != nil {
|
||||||
if err != nil {
|
errs = append(errs, fmt.Errorf("%w %q: %w", ErrInvalidHostpost, b.HTTPS, err))
|
||||||
errs = append(errs, fmt.Errorf("%w %q: %w", ErrCantBindToPort, b.HTTPS, err))
|
|
||||||
} else {
|
|
||||||
defer ln.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ln, err = net.Listen("tcp", b.Metrics)
|
if _, _, err := net.SplitHostPort(b.Metrics); err != nil {
|
||||||
if err != nil {
|
errs = append(errs, fmt.Errorf("%w %q: %w", ErrInvalidHostpost, b.Metrics, err))
|
||||||
errs = append(errs, fmt.Errorf("%w %q: %w", ErrCantBindToPort, b.Metrics, err))
|
|
||||||
} else {
|
|
||||||
defer ln.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestBindValid(t *testing.T) {
|
|||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reused ports",
|
name: "invalid ports",
|
||||||
precondition: func(t *testing.T) {
|
precondition: func(t *testing.T) {
|
||||||
ln, err := net.Listen("tcp", ":8081")
|
ln, err := net.Listen("tcp", ":8081")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,11 +33,11 @@ func TestBindValid(t *testing.T) {
|
|||||||
t.Cleanup(func() { ln.Close() })
|
t.Cleanup(func() { ln.Close() })
|
||||||
},
|
},
|
||||||
bind: Bind{
|
bind: Bind{
|
||||||
HTTP: ":8081",
|
HTTP: "",
|
||||||
HTTPS: ":8081",
|
HTTPS: "",
|
||||||
Metrics: ":8081",
|
Metrics: "",
|
||||||
},
|
},
|
||||||
err: ErrCantBindToPort,
|
err: ErrInvalidHostpost,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ func Main(ctx context.Context, opts Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
rtr.opts = opts
|
||||||
|
go rtr.backgroundReloadConfig(ctx)
|
||||||
|
|
||||||
g, gCtx := errgroup.WithContext(ctx)
|
g, gCtx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,18 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
||||||
"github.com/TecharoHQ/anubis/internal"
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/internal/fingerprint"
|
"github.com/TecharoHQ/anubis/internal/fingerprint"
|
||||||
"github.com/felixge/httpsnoop"
|
"github.com/felixge/httpsnoop"
|
||||||
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
"github.com/lum8rjack/go-ja4h"
|
"github.com/lum8rjack/go-ja4h"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
@@ -52,6 +57,7 @@ type Router struct {
|
|||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
routes map[string]http.Handler
|
routes map[string]http.Handler
|
||||||
tlsCerts map[string]*tls.Certificate
|
tlsCerts map[string]*tls.Certificate
|
||||||
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rtr *Router) setConfig(c config.Toplevel) error {
|
func (rtr *Router) setConfig(c config.Toplevel) error {
|
||||||
@@ -143,6 +149,48 @@ func (rtr *Router) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
|||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rtr *Router) loadConfig() error {
|
||||||
|
slog.Info("reloading config", "fname", rtr.opts.ConfigFname)
|
||||||
|
var cfg config.Toplevel
|
||||||
|
if err := hclsimple.DecodeFile(rtr.opts.ConfigFname, nil, &cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.Valid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rtr.setConfig(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("done!")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtr *Router) backgroundReloadConfig(ctx context.Context) {
|
||||||
|
t := time.NewTicker(time.Hour)
|
||||||
|
defer t.Stop()
|
||||||
|
ch := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(ch, syscall.SIGHUP)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
if err := rtr.loadConfig(); err != nil {
|
||||||
|
slog.Error("can't reload config", "fname", rtr.opts.ConfigFname, "err", err)
|
||||||
|
}
|
||||||
|
case <-ch:
|
||||||
|
if err := rtr.loadConfig(); err != nil {
|
||||||
|
slog.Error("can't reload config", "fname", rtr.opts.ConfigFname, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewRouter(c config.Toplevel) (*Router, error) {
|
func NewRouter(c config.Toplevel) (*Router, error) {
|
||||||
result := &Router{
|
result := &Router{
|
||||||
routes: map[string]http.Handler{},
|
routes: map[string]http.Handler{},
|
||||||
|
|||||||
Reference in New Issue
Block a user