Compare commits
2 Commits
v2024.3.29
...
v2024.4.19
Author | SHA1 | Date | |
---|---|---|---|
bb5796ab8c | |||
83fcb9a39d |
@ -5,14 +5,16 @@
|
|||||||
### Déploiement mono-noeud
|
### Déploiement mono-noeud
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Terminologie
|
## Terminologie
|
||||||
|
|
||||||
Voici une liste des termes utilisés dans le lexique Bouncer.
|
Voici une liste des termes utilisés dans le lexique Bouncer.
|
||||||
|
|
||||||
### Proxy
|
### Proxy
|
||||||
|
|
||||||
Un "proxy" est une entité logique définissant le relation suivante:
|
Un "proxy" est une entité logique définissant le relation suivante:
|
||||||
|
|
||||||
- Un ou plusieurs patrons de filtrage sous la forme `<host>:<port>`. Ceux ci identifient le ou les domaines associés à l'entité;
|
- Un ou plusieurs patrons de filtrage sous la forme d'un patron d'URL avec le caractère `*` comme caractère générique. Ceux ci identifient le ou les domaines/chemins associés à l'entité;
|
||||||
- Une URL cible qui servira de base pour la réécriture des requêtes.
|
- Une URL cible qui servira de base pour la réécriture des requêtes.
|
||||||
|
|
||||||
Un "proxy" peut avoir zéro ou plusieurs "layers" associés.
|
Un "proxy" peut avoir zéro ou plusieurs "layers" associés.
|
||||||
@ -27,4 +29,4 @@ Un "layer" (calque) est une entité logique définissant un traitement à appliq
|
|||||||
|
|
||||||
Un "layer" peut être activé ou désactivé.
|
Un "layer" peut être activé ou désactivé.
|
||||||
|
|
||||||
Un "layer" a un poids qui définit son niveau de priorité dans la pile de traitement (plus son poids est élevé plus il est prioritaire).
|
Un "layer" a un poids qui définit son niveau de priorité dans la pile de traitement (plus son poids est élevé plus il est prioritaire).
|
||||||
|
@ -147,7 +147,9 @@ Lister les proxies existants
|
|||||||
{
|
{
|
||||||
"name": "myproxy",
|
"name": "myproxy",
|
||||||
"weight": 0,
|
"weight": 0,
|
||||||
"enabled": false
|
"enabled": false,
|
||||||
|
"createdAt": "2018-12-10T13:45:00.000Z",
|
||||||
|
"updatedAt": "2018-12-10T13:45:00.000Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -179,3 +181,169 @@ Supprimer le proxy
|
|||||||
#### Source
|
#### Source
|
||||||
|
|
||||||
Voir [`internal/admin/proxy_route.go#deleteProxy()`](../../../internal/admin/proxy_route.go#deleteProxy)
|
Voir [`internal/admin/proxy_route.go#deleteProxy()`](../../../internal/admin/proxy_route.go#deleteProxy)
|
||||||
|
|
||||||
|
### `POST /api/v1/proxies/{proxyName}/layers`
|
||||||
|
|
||||||
|
Créer un nouveau layer pour un proxy donné
|
||||||
|
|
||||||
|
#### Paramètres
|
||||||
|
|
||||||
|
- `{proxyName}` - Nom du proxy sur lequel créer le layer
|
||||||
|
|
||||||
|
#### Exemple de corps de requête
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "mylayer", // OBLIGATOIRE - Nom du layer
|
||||||
|
"type": "<layer_type>", // OBLIGATOIRE - Type du layer, voir doc/fr/references/layers
|
||||||
|
"options": {} // OPTIONNEL - Options associées au layer, voir doc/fr/references/layers
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exemple de résultat
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"layer": {
|
||||||
|
"name": "mylayer",
|
||||||
|
"type": "<layer_type>",
|
||||||
|
"enabled": false,
|
||||||
|
"weight": 0,
|
||||||
|
"options": {},
|
||||||
|
"createdAt": "2018-12-10T13:45:00.000Z",
|
||||||
|
"updatedAt": "2018-12-10T13:45:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
Voir [`internal/admin/layer_route.go#createLayer()`](../../../internal/admin/layer_route.go#createLayer)
|
||||||
|
|
||||||
|
### `GET /api/v1/proxies/{proxyName}/layers/{layerName}`
|
||||||
|
|
||||||
|
Récupérer les informations complètes sur un layer
|
||||||
|
|
||||||
|
#### Paramètres
|
||||||
|
|
||||||
|
- `{proxyName}` - Nom du proxy parent
|
||||||
|
- `{layerName}` - Nom du layer
|
||||||
|
|
||||||
|
#### Exemple de résultat
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"layer": {
|
||||||
|
"name": "mylayer",
|
||||||
|
"type": "<layer_type>",
|
||||||
|
"enabled": false,
|
||||||
|
"weight": 0,
|
||||||
|
"options": {},
|
||||||
|
"createdAt": "2018-12-10T13:45:00.000Z",
|
||||||
|
"updatedAt": "2018-12-10T13:45:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
Voir [`internal/admin/layer_route.go#getLayer()`](../../../internal/admin/layer_route.go#getLayer)
|
||||||
|
|
||||||
|
### `PUT /api/v1/proxies/{proxyName}/layers/{layerName}`
|
||||||
|
|
||||||
|
Modifier un layer
|
||||||
|
|
||||||
|
#### Paramètres
|
||||||
|
|
||||||
|
- `{proxyName}` - Nom du proxy parent
|
||||||
|
- `{layerName}` - Nom du layer
|
||||||
|
|
||||||
|
#### Exemple de corps de requête
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"weight": 100, // OPTIONNEL - Poids à associer au layer
|
||||||
|
"enabled": true, // OPTIONNEL - Activer/désactiver le layer
|
||||||
|
"options": {} // OPTIONNEL - Modifier les options associées au layer, voir doc/fr/references/layers
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exemple de résultat
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"layer": {
|
||||||
|
"name": "mylayer",
|
||||||
|
"type": "<layer_type>",
|
||||||
|
"enabled": false,
|
||||||
|
"weight": 0,
|
||||||
|
"options": {},
|
||||||
|
"createdAt": "2018-12-10T13:45:00.000Z",
|
||||||
|
"updatedAt": "2018-12-10T13:45:00.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
Voir [`internal/admin/layer_route.go#updateLayer()`](../../../internal/admin/layer_route.go#updateLayer)
|
||||||
|
|
||||||
|
### `GET /api/v1/proxies/{proxyName}/layers?names={name1,name2,...}`
|
||||||
|
|
||||||
|
Lister les layers existants
|
||||||
|
|
||||||
|
#### Paramètres
|
||||||
|
|
||||||
|
- `{proxyName}` - Nom du proxy parent
|
||||||
|
- `{names}` - Optionnel - Liste des noms de proxy à appliquer en tant que filtre
|
||||||
|
|
||||||
|
#### Exemple de résultat
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"layers": [
|
||||||
|
{
|
||||||
|
"name": "mylayer",
|
||||||
|
"weight": 0,
|
||||||
|
"enabled": false,
|
||||||
|
"createdAt": "2018-12-10T13:45:00.000Z",
|
||||||
|
"updatedAt": "2018-12-10T13:45:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
Voir [`internal/admin/layer_route.go#queryLayers()`](../../../internal/admin/layer_route.go#queryLayers)
|
||||||
|
|
||||||
|
## `DELETE /api/v1/proxies/{proxyName}/layers/{layerName}`
|
||||||
|
|
||||||
|
Supprimer le layer
|
||||||
|
|
||||||
|
#### Paramètres
|
||||||
|
|
||||||
|
- `{proxyName}` - Nom du proxy parent
|
||||||
|
- `{layerName}` - Nom du layer
|
||||||
|
|
||||||
|
#### Exemple de résultat
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"layerName": "mylayer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Source
|
||||||
|
|
||||||
|
Voir [`internal/admin/layer_route.go#deleteLayer()`](../../../internal/admin/layer_route.go#deleteLayer)
|
||||||
|
@ -22,7 +22,7 @@ func (s *Server) bootstrapProxies(ctx context.Context) error {
|
|||||||
layerRepo := s.layerRepository
|
layerRepo := s.layerRepository
|
||||||
|
|
||||||
lockTimeout := time.Duration(s.bootstrapConfig.LockTimeout)
|
lockTimeout := time.Duration(s.bootstrapConfig.LockTimeout)
|
||||||
locker := redis.NewLocker(s.redisClient)
|
locker := redis.NewLocker(s.redisClient, int(s.bootstrapConfig.MaxConnectionRetries))
|
||||||
|
|
||||||
err := locker.WithLock(ctx, "bouncer-admin-bootstrap", lockTimeout, func(ctx context.Context) error {
|
err := locker.WithLock(ctx, "bouncer-admin-bootstrap", lockTimeout, func(ctx context.Context) error {
|
||||||
logger.Info(ctx, "bootstrapping proxies")
|
logger.Info(ctx, "bootstrapping proxies")
|
||||||
|
@ -12,9 +12,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type BootstrapConfig struct {
|
type BootstrapConfig struct {
|
||||||
Proxies map[store.ProxyName]BootstrapProxyConfig `yaml:"proxies"`
|
Proxies map[store.ProxyName]BootstrapProxyConfig `yaml:"proxies"`
|
||||||
Dir InterpolatedString `yaml:"dir"`
|
Dir InterpolatedString `yaml:"dir"`
|
||||||
LockTimeout InterpolatedDuration `yaml:"lockTimeout"`
|
LockTimeout InterpolatedDuration `yaml:"lockTimeout"`
|
||||||
|
MaxConnectionRetries InterpolatedInt `yaml:"maxRetries"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BootstrapConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (c *BootstrapConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
@ -62,8 +63,9 @@ type BootstrapLayerConfig struct {
|
|||||||
|
|
||||||
func NewDefaultBootstrapConfig() BootstrapConfig {
|
func NewDefaultBootstrapConfig() BootstrapConfig {
|
||||||
return BootstrapConfig{
|
return BootstrapConfig{
|
||||||
Dir: "",
|
Dir: "",
|
||||||
LockTimeout: *NewInterpolatedDuration(30 * time.Second),
|
LockTimeout: *NewInterpolatedDuration(30 * time.Second),
|
||||||
|
MaxConnectionRetries: 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,19 +9,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type RedisConfig struct {
|
type RedisConfig struct {
|
||||||
Adresses InterpolatedStringSlice `yaml:"addresses"`
|
Adresses InterpolatedStringSlice `yaml:"addresses"`
|
||||||
Master InterpolatedString `yaml:"master"`
|
Master InterpolatedString `yaml:"master"`
|
||||||
ReadTimeout InterpolatedDuration `yaml:"readTimeout"`
|
ReadTimeout InterpolatedDuration `yaml:"readTimeout"`
|
||||||
WriteTimeout InterpolatedDuration `yaml:"writeTimeout"`
|
WriteTimeout InterpolatedDuration `yaml:"writeTimeout"`
|
||||||
DialTimeout InterpolatedDuration `yaml:"dialTimeout"`
|
DialTimeout InterpolatedDuration `yaml:"dialTimeout"`
|
||||||
|
LockMaxRetries InterpolatedInt `yaml:"lockMaxRetries"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultRedisConfig() RedisConfig {
|
func NewDefaultRedisConfig() RedisConfig {
|
||||||
return RedisConfig{
|
return RedisConfig{
|
||||||
Adresses: InterpolatedStringSlice{"localhost:6379"},
|
Adresses: InterpolatedStringSlice{"localhost:6379"},
|
||||||
Master: "",
|
Master: "",
|
||||||
ReadTimeout: InterpolatedDuration(30 * time.Second),
|
ReadTimeout: InterpolatedDuration(30 * time.Second),
|
||||||
WriteTimeout: InterpolatedDuration(30 * time.Second),
|
WriteTimeout: InterpolatedDuration(30 * time.Second),
|
||||||
DialTimeout: InterpolatedDuration(30 * time.Second),
|
DialTimeout: InterpolatedDuration(30 * time.Second),
|
||||||
|
LockMaxRetries: 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Locker struct {
|
type Locker struct {
|
||||||
client redis.UniversalClient
|
client redis.UniversalClient
|
||||||
timeout time.Duration
|
maxRetries int
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLock implements lock.Locker.
|
// WithLock implements lock.Locker.
|
||||||
@ -26,33 +26,41 @@ func (l *Locker) WithLock(ctx context.Context, key string, timeout time.Duration
|
|||||||
|
|
||||||
logger.Debug(ctx, "acquiring lock")
|
logger.Debug(ctx, "acquiring lock")
|
||||||
|
|
||||||
lock, err := locker.Obtain(ctx, key, timeout, &redislock.Options{
|
err := retryWithBackoff(ctx, l.maxRetries, func(ctx context.Context) error {
|
||||||
RetryStrategy: backoff,
|
lock, err := locker.Obtain(ctx, key, timeout, &redislock.Options{
|
||||||
})
|
RetryStrategy: backoff,
|
||||||
if err != nil {
|
})
|
||||||
return errors.WithStack(err)
|
if err != nil {
|
||||||
}
|
return errors.WithStack(err)
|
||||||
|
|
||||||
logger.Debug(ctx, "lock obtained")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := lock.Release(ctx); err != nil {
|
|
||||||
logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug(ctx, "lock released")
|
logger.Debug(ctx, "lock obtained")
|
||||||
}()
|
|
||||||
|
|
||||||
if err := fn(ctx); err != nil {
|
defer func() {
|
||||||
|
if err := lock.Release(ctx); err != nil {
|
||||||
|
logger.Error(ctx, "could not release lock", logger.E(errors.WithStack(err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug(ctx, "lock released")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := fn(ctx); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocker(client redis.UniversalClient) *Locker {
|
func NewLocker(client redis.UniversalClient, maxRetries int) *Locker {
|
||||||
return &Locker{
|
return &Locker{
|
||||||
client: client,
|
client: client,
|
||||||
|
maxRetries: maxRetries,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
internal/lock/redis/retry.go
Normal file
42
internal/lock/redis/retry.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
baseWatchBackoffDelay = time.Millisecond * 500
|
||||||
|
maxDelay = time.Minute * 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func retryWithBackoff(ctx context.Context, attempts int, fn func(ctx context.Context) error) error {
|
||||||
|
backoffDelay := baseWatchBackoffDelay
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
err := fn(ctx)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
|
||||||
|
count++
|
||||||
|
if count >= attempts {
|
||||||
|
return errors.Wrapf(err, "execution failed after %d attempts", attempts)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Error(ctx, "error while executing func, retrying with backoff", logger.E(err), logger.F("backoffDelay", backoffDelay), logger.F("remainingAttempts", attempts-count))
|
||||||
|
|
||||||
|
time.Sleep(backoffDelay)
|
||||||
|
|
||||||
|
backoffDelay *= 2
|
||||||
|
if backoffDelay > maxDelay {
|
||||||
|
backoffDelay = maxDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ func SetupIntegrations(ctx context.Context, conf *config.Config) ([]integration.
|
|||||||
|
|
||||||
func setupKubernetesIntegration(ctx context.Context, conf *config.Config) (*kubernetes.Integration, error) {
|
func setupKubernetesIntegration(ctx context.Context, conf *config.Config) (*kubernetes.Integration, error) {
|
||||||
client := newRedisClient(conf.Redis)
|
client := newRedisClient(conf.Redis)
|
||||||
locker := redis.NewLocker(client)
|
locker := redis.NewLocker(client, 10)
|
||||||
|
|
||||||
integration := kubernetes.NewIntegration(
|
integration := kubernetes.NewIntegration(
|
||||||
kubernetes.WithReaderTokenSecret(string(conf.Integrations.Kubernetes.ReaderTokenSecret)),
|
kubernetes.WithReaderTokenSecret(string(conf.Integrations.Kubernetes.ReaderTokenSecret)),
|
||||||
|
@ -10,6 +10,6 @@ import (
|
|||||||
|
|
||||||
func SetupLocker(ctx context.Context, conf *config.Config) (lock.Locker, error) {
|
func SetupLocker(ctx context.Context, conf *config.Config) (lock.Locker, error) {
|
||||||
client := newRedisClient(conf.Redis)
|
client := newRedisClient(conf.Redis)
|
||||||
locker := redis.NewLocker(client)
|
locker := redis.NewLocker(client, int(conf.Redis.LockMaxRetries))
|
||||||
return locker, nil
|
return locker, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user