Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mw plugins #750

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions proxy/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func NewPluginMiddleware(logger logging.Logger, endpoint *config.EndpointConfig)
return emptyMiddlewareFallback(logger)
}

return newPluginMiddleware(logger, "ENDPOINT", endpoint.Endpoint, cfg)
return newModifierPluginMiddleware(logger, "ENDPOINT", endpoint.Endpoint, cfg)
}

// NewBackendPluginMiddleware returns a backend middleware wrapped (if required) with the plugin middleware.
Expand All @@ -38,11 +38,11 @@ func NewBackendPluginMiddleware(logger logging.Logger, remote *config.Backend) M
return emptyMiddlewareFallback(logger)
}

return newPluginMiddleware(logger, "BACKEND",
return newModifierPluginMiddleware(logger, "BACKEND",
fmt.Sprintf("%s %s -> %s", remote.ParentEndpointMethod, remote.ParentEndpoint, remote.URLPattern), cfg)
}

func newPluginMiddleware(logger logging.Logger, tag, pattern string, cfg map[string]interface{}) Middleware {
func newModifierPluginMiddleware(logger logging.Logger, tag, pattern string, cfg map[string]interface{}) Middleware {
plugins, ok := cfg["name"].([]interface{})
if !ok {
return emptyMiddlewareFallback(logger)
Expand Down
108 changes: 108 additions & 0 deletions proxy/plugin/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: Apache-2.0

package plugin

import (
"context"
"fmt"

"github.com/luraproject/lura/v2/logging"
luraplugin "github.com/luraproject/lura/v2/plugin"
)

// MiddlewareNamespace is the internal namespace for the register to be used with middlewares
const MiddlewareNamespace = "github.com/devopsfaith/krakend/proxy/plugin/middleware"

// MiddlewareFactory is function that, given an extra_config and a middleware returns another middleware with custom logic wrapping the one passed as argument
type MiddlewareFactory func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error)

// RegisterModifier registers the injected modifier factory with the given name at the selected namespace
func RegisterMiddleware(
name string,
middlewareFactory func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error),
) {
pluginRegister.Register(MiddlewareNamespace, name, middlewareFactory)
}

// MiddlewareRegisterer defines the interface for the plugins to expose in order to be able to be loaded/registered
type MiddlewareRegisterer interface {
RegisterMiddlewares(func(
name string,
middlewareFactory func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error),
))
}

type RegisterMiddlewareFunc func(
name string,
middlewareFactory func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error),
)

func GetMiddleware(name string) (MiddlewareFactory, bool) {
r, ok := pluginRegister.Get(MiddlewareNamespace)
if !ok {
return nil, ok
}
m, ok := r.Get(name)
if !ok {
return nil, ok
}
res, ok := m.(func(map[string]interface{}, func(context.Context, interface{}) (interface{}, error)) func(context.Context, interface{}) (interface{}, error))
if !ok {
return nil, ok
}
return MiddlewareFactory(res), ok
}

func LoadMiddlewares(ctx context.Context, path, pattern string, rmf RegisterMiddlewareFunc, logger logging.Logger) (int, error) {
plugins, err := luraplugin.Scan(path, pattern)
if err != nil {
return 0, err
}

var errors []error

loadedPlugins := 0
for k, pluginName := range plugins {
if err := openMiddleware(ctx, pluginName, rmf, logger); err != nil {
errors = append(errors, fmt.Errorf("plugin #%d (%s): %s", k, pluginName, err.Error()))
continue
}
loadedPlugins++
}

if len(errors) > 0 {
return loadedPlugins, loaderError{errors: errors}
}

return loadedPlugins, nil
}

func openMiddleware(ctx context.Context, pluginName string, rmf RegisterMiddlewareFunc, logger logging.Logger) (err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
}
}()

p, err := pluginOpener(pluginName)
if err != nil {
return err
}
r, err := p.Lookup("MiddlewareRegisterer")
if err != nil {
return err
}
registerer, ok := r.(MiddlewareRegisterer)
if !ok {
return fmt.Errorf("middleware plugin loader: unknown type")
}

registerExtraComponents(r, ctx, logger)

registerer.RegisterMiddlewares(rmf)
return
}
73 changes: 12 additions & 61 deletions proxy/plugin/modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,20 @@ package plugin
import (
"context"
"fmt"
"plugin"
"strings"

"github.com/luraproject/lura/v2/logging"
luraplugin "github.com/luraproject/lura/v2/plugin"
"github.com/luraproject/lura/v2/register"
)

const (
// Namespace is the namespace for the extra_config section
Namespace = "github.com/devopsfaith/krakend/proxy/plugin"
// requestNamespace is the internal namespace for the register to be used with request modifiers
requestNamespace = "github.com/devopsfaith/krakend/proxy/plugin/request"
// responseNamespace is the internal namespace for the register to be used with response modifiers
responseNamespace = "github.com/devopsfaith/krakend/proxy/plugin/response"
)

var modifierRegister = register.New()
var pluginRegister = register.New()

// ModifierFactory is a function that, given a config passed as a map, returns a modifier
type ModifierFactory func(map[string]interface{}) func(interface{}) (interface{}, error)
Expand All @@ -41,7 +37,7 @@ func GetResponseModifier(name string) (ModifierFactory, bool) {
}

func getModifier(namespace, name string) (ModifierFactory, bool) {
r, ok := modifierRegister.Get(namespace)
r, ok := pluginRegister.Get(namespace)
if !ok {
return nil, ok
}
Expand All @@ -64,10 +60,10 @@ func RegisterModifier(
appliesToResponse bool,
) {
if appliesToRequest {
modifierRegister.Register(requestNamespace, name, modifierFactory)
pluginRegister.Register(requestNamespace, name, modifierFactory)
}
if appliesToResponse {
modifierRegister.Register(responseNamespace, name, modifierFactory)
pluginRegister.Register(responseNamespace, name, modifierFactory)
}
}

Expand Down Expand Up @@ -112,15 +108,12 @@ func LoadWithLoggerAndContext(ctx context.Context, path, pattern string, rmf Reg
if err != nil {
return 0, err
}
return load(ctx, plugins, rmf, logger)
}

func load(ctx context.Context, plugins []string, rmf RegisterModifierFunc, logger logging.Logger) (int, error) {
var errors []error

loadedPlugins := 0
for k, pluginName := range plugins {
if err := open(ctx, pluginName, rmf, logger); err != nil {
if err := openModifier(ctx, pluginName, rmf, logger); err != nil {
errors = append(errors, fmt.Errorf("plugin #%d (%s): %s", k, pluginName, err.Error()))
continue
}
Expand All @@ -130,10 +123,11 @@ func load(ctx context.Context, plugins []string, rmf RegisterModifierFunc, logge
if len(errors) > 0 {
return loadedPlugins, loaderError{errors: errors}
}

return loadedPlugins, nil
}

func open(ctx context.Context, pluginName string, rmf RegisterModifierFunc, logger logging.Logger) (err error) {
func openModifier(ctx context.Context, pluginName string, rmf RegisterModifierFunc, logger logging.Logger) (err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
Expand All @@ -144,64 +138,21 @@ func open(ctx context.Context, pluginName string, rmf RegisterModifierFunc, logg
}
}()

var p Plugin
p, err = pluginOpener(pluginName)
p, err := pluginOpener(pluginName)
if err != nil {
return
return err
}
var r interface{}
r, err = p.Lookup("ModifierRegisterer")
r, err := p.Lookup("ModifierRegisterer")
if err != nil {
return
return err
}
registerer, ok := r.(Registerer)
if !ok {
return fmt.Errorf("modifier plugin loader: unknown type")
}

if logger != nil {
if lr, ok := r.(LoggerRegisterer); ok {
lr.RegisterLogger(logger)
}
}

if lr, ok := r.(ContextRegisterer); ok {
lr.RegisterContext(ctx)
}
registerExtraComponents(r, ctx, logger)

registerer.RegisterModifiers(rmf)
return
}

// Plugin is the interface of the loaded plugins
type Plugin interface {
Lookup(name string) (plugin.Symbol, error)
}

// pluginOpener keeps the plugin open function in a var for easy testing
var pluginOpener = defaultPluginOpener

func defaultPluginOpener(name string) (Plugin, error) {
return plugin.Open(name)
}

type loaderError struct {
errors []error
}

// Error implements the error interface
func (l loaderError) Error() string {
msgs := make([]string, len(l.errors))
for i, err := range l.errors {
msgs[i] = err.Error()
}
return fmt.Sprintf("plugin loader found %d error(s): \n%s", len(msgs), strings.Join(msgs, "\n"))
}

func (l loaderError) Len() int {
return len(l.errors)
}

func (l loaderError) Errs() []error {
return l.errors
}
60 changes: 60 additions & 0 deletions proxy/plugin/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: Apache-2.0

package plugin

import (
"context"
"fmt"
"plugin"
"strings"

"github.com/luraproject/lura/v2/logging"
)

// Namespace is the namespace for the extra_config section
const Namespace = "github.com/devopsfaith/krakend/proxy/plugin"

// Plugin is the interface of the loaded plugins
type Plugin interface {
Lookup(name string) (plugin.Symbol, error)
}

// pluginOpener keeps the plugin open function in a var for easy testing
var pluginOpener = defaultPluginOpener

func defaultPluginOpener(name string) (Plugin, error) {
return plugin.Open(name)
}

func registerExtraComponents(r plugin.Symbol, ctx context.Context, l logging.Logger) {
if l != nil {
if lr, ok := r.(LoggerRegisterer); ok {
lr.RegisterLogger(l)
}
}

if lr, ok := r.(ContextRegisterer); ok {
lr.RegisterContext(ctx)
}
}

type loaderError struct {
errors []error
}

// Error implements the error interface
func (l loaderError) Error() string {
msgs := make([]string, len(l.errors))
for i, err := range l.errors {
msgs[i] = err.Error()
}
return fmt.Sprintf("plugin loader found %d error(s): \n%s", len(msgs), strings.Join(msgs, "\n"))
}

func (l loaderError) Len() int {
return len(l.errors)
}

func (l loaderError) Errs() []error {
return l.errors
}
Loading