feat: refactor layers registration
Cadoles/bouncer/pipeline/head This commit looks good
Details
Cadoles/bouncer/pipeline/head This commit looks good
Details
This commit is contained in:
parent
e32c72e030
commit
8d91f646c2
|
@ -245,7 +245,19 @@ func (s *Server) updateLayer(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateLayerReq.Options != nil {
|
if updateLayerReq.Options != nil {
|
||||||
if err := schema.ValidateLayerOptions(ctx, layer.Type, updateLayerReq.Options); err != nil {
|
layerOptionsSchema, err := setup.GetLayerOptionsSchema(layer.Type)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(r.Context(), "could not retrieve layer options schema", logger.E(errors.WithStack(err)))
|
||||||
|
api.ErrorResponse(w, http.StatusInternalServerError, api.ErrCodeUnknownError, nil)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawOptions := func(opts *store.LayerOptions) map[string]any {
|
||||||
|
return *opts
|
||||||
|
}(updateLayerReq.Options)
|
||||||
|
|
||||||
|
if err := schema.Validate(ctx, layerOptionsSchema, rawOptions); err != nil {
|
||||||
logger.Error(r.Context(), "could not validate layer options", logger.E(errors.WithStack(err)))
|
logger.Error(r.Context(), "could not validate layer options", logger.E(errors.WithStack(err)))
|
||||||
|
|
||||||
var invalidDataErr *schema.InvalidDataError
|
var invalidDataErr *schema.InvalidDataError
|
||||||
|
|
|
@ -28,7 +28,7 @@ func RunCommand() *cli.Command {
|
||||||
logger.SetFormat(logger.Format(conf.Logger.Format))
|
logger.SetFormat(logger.Format(conf.Logger.Format))
|
||||||
logger.SetLevel(logger.Level(conf.Logger.Level))
|
logger.SetLevel(logger.Level(conf.Logger.Level))
|
||||||
|
|
||||||
layers, err := setup.CreateLayers(ctx.Context, conf)
|
layers, err := setup.GetLayers(ctx.Context, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not initialize director layers")
|
return errors.Wrap(err, "could not initialize director layers")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/queue"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed schema/layer-options.json
|
||||||
|
var RawLayerOptionsSchema []byte
|
|
@ -1,20 +0,0 @@
|
||||||
package queue
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/schema"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed schema/layer-options.json
|
|
||||||
var rawLayerOptionsSchema []byte
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
layerOptionsSchema, err := schema.Parse(rawLayerOptionsSchema)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "could not parse queue layer options schema"))
|
|
||||||
}
|
|
||||||
|
|
||||||
schema.RegisterLayerOptionsSchema(LayerType, layerOptionsSchema)
|
|
||||||
}
|
|
|
@ -7,8 +7,10 @@ import (
|
||||||
"github.com/qri-io/jsonschema"
|
"github.com/qri-io/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Parse(data []byte) (*jsonschema.Schema, error) {
|
type Schema = jsonschema.Schema
|
||||||
var schema jsonschema.Schema
|
|
||||||
|
func Parse(data []byte) (*Schema, error) {
|
||||||
|
var schema Schema
|
||||||
if err := json.Unmarshal(data, &schema); err != nil {
|
if err := json.Unmarshal(data, &schema); err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/qri-io/jsonschema"
|
|
||||||
)
|
|
||||||
|
|
||||||
var defaultRegistry = NewRegistry()
|
|
||||||
|
|
||||||
func RegisterLayerOptionsSchema(layerType store.LayerType, schema *jsonschema.Schema) {
|
|
||||||
defaultRegistry.RegisterLayerOptionsSchema(layerType, schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateLayerOptions(ctx context.Context, layerType store.LayerType, options *store.LayerOptions) error {
|
|
||||||
if err := defaultRegistry.ValidateLayerOptions(ctx, layerType, options); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Registry struct {
|
|
||||||
layerOptionSchemas map[store.LayerType]*jsonschema.Schema
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Registry) RegisterLayerOptionsSchema(layerType store.LayerType, schema *jsonschema.Schema) {
|
|
||||||
r.layerOptionSchemas[layerType] = schema
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Registry) ValidateLayerOptions(ctx context.Context, layerType store.LayerType, options *store.LayerOptions) error {
|
|
||||||
schema, exists := r.layerOptionSchemas[layerType]
|
|
||||||
if !exists {
|
|
||||||
return errors.WithStack(ErrSchemaNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
rawOptions := func(opts *store.LayerOptions) map[string]any {
|
|
||||||
return *opts
|
|
||||||
}(options)
|
|
||||||
|
|
||||||
state := schema.Validate(ctx, rawOptions)
|
|
||||||
|
|
||||||
if len(*state.Errs) > 0 {
|
|
||||||
return errors.WithStack(NewInvalidDataError(*state.Errs...))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegistry() *Registry {
|
|
||||||
return &Registry{
|
|
||||||
layerOptionSchemas: make(map[store.LayerType]*jsonschema.Schema),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package schema
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Validate(ctx context.Context, schema *Schema, data map[string]any) error {
|
||||||
|
state := schema.Validate(ctx, data)
|
||||||
|
|
||||||
|
if len(*state.Errs) > 0 {
|
||||||
|
return errors.WithStack(NewInvalidDataError(*state.Errs...))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -7,16 +7,26 @@ import (
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/qri-io/jsonschema"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultRegistry = NewRegistry()
|
var defaultRegistry = NewRegistry()
|
||||||
|
|
||||||
func RegisterLayer(layerType store.LayerType, setupFunc LayerSetupFunc) {
|
func RegisterLayer(layerType store.LayerType, setupFunc LayerSetupFunc, rawOptionsSchema []byte) {
|
||||||
defaultRegistry.RegisterLayer(layerType, setupFunc)
|
defaultRegistry.RegisterLayer(layerType, setupFunc, rawOptionsSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateLayers(ctx context.Context, conf *config.Config) ([]director.Layer, error) {
|
func GetLayerOptionsSchema(layerType store.LayerType) (*jsonschema.Schema, error) {
|
||||||
layers, err := defaultRegistry.CreateLayers(ctx, conf)
|
schema, err := defaultRegistry.GetLayerOptionsSchema(layerType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLayers(ctx context.Context, conf *config.Config) ([]director.Layer, error) {
|
||||||
|
layers, err := defaultRegistry.GetLayers(ctx, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package setup
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var ErrNotFound = errors.New("not found")
|
|
@ -1,21 +1,22 @@
|
||||||
package setup
|
package setup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/queue"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue"
|
||||||
|
queueRedis "forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/queue/redis"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterLayer(queue.LayerType, setupQueueLayer)
|
RegisterLayer(queue.LayerType, setupQueueLayer, queue.RawLayerOptionsSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupQueueLayer(ctx context.Context, conf *config.Config) (director.Layer, error) {
|
func setupQueueLayer(conf *config.Config) (director.Layer, error) {
|
||||||
adapter, err := NewQueueAdapter(ctx, conf.Redis)
|
adapter, err := newQueueAdapter(conf.Redis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -33,3 +34,12 @@ func setupQueueLayer(ctx context.Context, conf *config.Config) (director.Layer,
|
||||||
options...,
|
options...,
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newQueueAdapter(redisConf config.RedisConfig) (queue.Adapter, error) {
|
||||||
|
rdb := redis.NewUniversalClient(&redis.UniversalOptions{
|
||||||
|
Addrs: redisConf.Adresses,
|
||||||
|
MasterName: string(redisConf.Master),
|
||||||
|
})
|
||||||
|
|
||||||
|
return queueRedis.NewAdapter(rdb, 2), nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package setup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/queue"
|
|
||||||
"github.com/redis/go-redis/v9"
|
|
||||||
|
|
||||||
queueRedis "forge.cadoles.com/cadoles/bouncer/internal/queue/redis"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewQueueAdapter(ctx context.Context, redisConf config.RedisConfig) (queue.Adapter, error) {
|
|
||||||
rdb := redis.NewUniversalClient(&redis.UniversalOptions{
|
|
||||||
Addrs: redisConf.Adresses,
|
|
||||||
MasterName: string(redisConf.Master),
|
|
||||||
})
|
|
||||||
|
|
||||||
return queueRedis.NewAdapter(rdb, 2), nil
|
|
||||||
}
|
|
|
@ -5,25 +5,48 @@ import (
|
||||||
|
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
"forge.cadoles.com/cadoles/bouncer/internal/config"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
"forge.cadoles.com/cadoles/bouncer/internal/proxy/director"
|
||||||
|
"forge.cadoles.com/cadoles/bouncer/internal/schema"
|
||||||
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
"forge.cadoles.com/cadoles/bouncer/internal/store"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type layerEntry struct {
|
||||||
|
setup LayerSetupFunc
|
||||||
|
rawOptionsSchema []byte
|
||||||
|
}
|
||||||
|
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
layers map[store.LayerType]LayerSetupFunc
|
layers map[store.LayerType]layerEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
type LayerSetupFunc func(context.Context, *config.Config) (director.Layer, error)
|
type LayerSetupFunc func(*config.Config) (director.Layer, error)
|
||||||
|
|
||||||
func (r *Registry) RegisterLayer(layerType store.LayerType, layerSetup LayerSetupFunc) {
|
func (r *Registry) RegisterLayer(layerType store.LayerType, layerSetup LayerSetupFunc, rawOptionsSchema []byte) {
|
||||||
r.layers[layerType] = layerSetup
|
r.layers[layerType] = layerEntry{
|
||||||
|
setup: layerSetup,
|
||||||
|
rawOptionsSchema: rawOptionsSchema,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) CreateLayers(ctx context.Context, conf *config.Config) ([]director.Layer, error) {
|
func (r *Registry) GetLayerOptionsSchema(layerType store.LayerType) (*schema.Schema, error) {
|
||||||
|
layerEntry, exists := r.layers[layerType]
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.WithStack(ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
schema, err := schema.Parse(layerEntry.rawOptionsSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) GetLayers(ctx context.Context, conf *config.Config) ([]director.Layer, error) {
|
||||||
layers := make([]director.Layer, 0, len(r.layers))
|
layers := make([]director.Layer, 0, len(r.layers))
|
||||||
|
|
||||||
for layerType, layerSetup := range r.layers {
|
for layerType, layerEntry := range r.layers {
|
||||||
layer, err := layerSetup(ctx, conf)
|
layer, err := layerEntry.setup(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "could not create layer '%s'", layerType)
|
return nil, errors.Wrapf(err, "could not create layer '%s'", layerType)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +75,6 @@ func (r *Registry) GetLayerTypes() []store.LayerType {
|
||||||
|
|
||||||
func NewRegistry() *Registry {
|
func NewRegistry() *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
layers: make(map[store.LayerType]LayerSetupFunc),
|
layers: make(map[store.LayerType]layerEntry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue