2023-04-24 20:52:12 +02:00
|
|
|
package admin
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-03-26 17:28:38 +01:00
|
|
|
"time"
|
2023-04-24 20:52:12 +02:00
|
|
|
|
2024-03-26 17:28:38 +01:00
|
|
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
|
|
|
"forge.cadoles.com/cadoles/bouncer/internal/schema"
|
2023-04-24 20:52:12 +02:00
|
|
|
"forge.cadoles.com/cadoles/bouncer/internal/setup"
|
2024-03-26 17:28:38 +01:00
|
|
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
|
|
|
"github.com/bsm/redislock"
|
2023-04-24 20:52:12 +02:00
|
|
|
"github.com/pkg/errors"
|
2024-03-26 17:28:38 +01:00
|
|
|
"gitlab.com/wpetit/goweb/logger"
|
2023-04-24 20:52:12 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func (s *Server) initRepositories(ctx context.Context) error {
|
2024-03-26 17:28:38 +01:00
|
|
|
if err := s.initRedisClient(ctx); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
2023-04-24 20:52:12 +02:00
|
|
|
if err := s.initLayerRepository(ctx); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.initProxyRepository(ctx); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-26 17:28:38 +01:00
|
|
|
func (s *Server) initRedisClient(ctx context.Context) error {
|
|
|
|
client := setup.NewRedisClient(ctx, s.redisConfig)
|
|
|
|
|
|
|
|
s.redisClient = client
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-24 20:52:12 +02:00
|
|
|
func (s *Server) initLayerRepository(ctx context.Context) error {
|
2024-03-26 17:28:38 +01:00
|
|
|
layerRepository, err := setup.NewLayerRepository(ctx, s.redisClient)
|
2023-04-24 20:52:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
s.layerRepository = layerRepository
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) initProxyRepository(ctx context.Context) error {
|
2024-03-26 17:28:38 +01:00
|
|
|
proxyRepository, err := setup.NewProxyRepository(ctx, s.redisClient)
|
2023-04-24 20:52:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
s.proxyRepository = proxyRepository
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-03-26 17:28:38 +01:00
|
|
|
|
|
|
|
const bootstrapLockKey = "bouncer-bootstrap"
|
|
|
|
|
|
|
|
func (s *Server) bootstrapProxies(ctx context.Context) error {
|
|
|
|
if err := s.validateBootstrap(ctx); err != nil {
|
|
|
|
return errors.Wrap(err, "could not validate bootstrapped proxies")
|
|
|
|
}
|
|
|
|
|
|
|
|
proxyRepo := s.proxyRepository
|
|
|
|
layerRepo := s.layerRepository
|
|
|
|
|
|
|
|
locker := redislock.New(s.redisClient)
|
|
|
|
|
|
|
|
backoff := redislock.ExponentialBackoff(time.Second, time.Duration(s.bootstrapConfig.LockTimeout)*2)
|
|
|
|
|
|
|
|
logger.Debug(ctx, "acquiring proxies bootstrap lock", logger.F("lockTimeout", s.bootstrapConfig.LockTimeout))
|
|
|
|
|
|
|
|
lock, err := locker.Obtain(ctx, bootstrapLockKey, time.Duration(s.bootstrapConfig.LockTimeout), &redislock.Options{
|
|
|
|
RetryStrategy: backoff,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := lock.Release(ctx); err != nil {
|
|
|
|
logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err)))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
logger.Info(ctx, "bootstrapping proxies")
|
|
|
|
|
|
|
|
for proxyName, proxyConfig := range s.bootstrapConfig.Proxies {
|
|
|
|
_, err := s.proxyRepository.GetProxy(ctx, proxyName)
|
|
|
|
if !errors.Is(err, store.ErrNotFound) {
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Info(ctx, "ignoring existing proxy", logger.F("proxyName", proxyName))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Info(ctx, "creating proxy", logger.F("proxyName", proxyName))
|
|
|
|
|
|
|
|
if _, err := proxyRepo.CreateProxy(ctx, proxyName, string(proxyConfig.To), proxyConfig.From...); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = proxyRepo.UpdateProxy(
|
|
|
|
ctx, proxyName,
|
|
|
|
store.WithProxyUpdateEnabled(bool(proxyConfig.Enabled)),
|
|
|
|
store.WithProxyUpdateWeight(int(proxyConfig.Weight)),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for layerName, layerConfig := range proxyConfig.Layers {
|
|
|
|
layerType := store.LayerType(layerConfig.Type)
|
|
|
|
layerOptions := store.LayerOptions(layerConfig.Options)
|
|
|
|
|
|
|
|
if _, err := layerRepo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := layerRepo.UpdateLayer(
|
|
|
|
ctx,
|
|
|
|
proxyName, layerName,
|
|
|
|
store.WithLayerUpdateEnabled(bool(layerConfig.Enabled)),
|
|
|
|
store.WithLayerUpdateOptions(layerOptions),
|
|
|
|
store.WithLayerUpdateWeight(int(layerConfig.Weight)),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const validateErrMessage = "could not validate proxy '%s': could not validate layer '%s'"
|
|
|
|
|
|
|
|
func (s *Server) validateBootstrap(ctx context.Context) error {
|
|
|
|
for proxyName, proxyConf := range s.bootstrapConfig.Proxies {
|
|
|
|
for layerName, layerConf := range proxyConf.Layers {
|
|
|
|
layerType := store.LayerType(layerConf.Type)
|
|
|
|
if !setup.LayerTypeExists(layerType) {
|
|
|
|
return errors.Errorf(validateErrMessage+": could not find layer type '%s'", proxyName, layerName, layerType)
|
|
|
|
}
|
|
|
|
|
|
|
|
layerOptionsSchema, err := setup.GetLayerOptionsSchema(layerType)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, validateErrMessage, proxyName, layerName)
|
|
|
|
}
|
|
|
|
|
|
|
|
rawOptions := func(opts config.InterpolatedMap) map[string]any {
|
|
|
|
return opts
|
|
|
|
}(layerConf.Options)
|
|
|
|
|
|
|
|
if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil {
|
|
|
|
return errors.Wrapf(err, validateErrMessage, proxyName, layerName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|