forked from motiv-labs/janus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.go
128 lines (106 loc) · 3.48 KB
/
plugin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package plugin
import (
"encoding/json"
"fmt"
"sync"
"github.com/hellofresh/janus/pkg/api"
"github.com/hellofresh/janus/pkg/proxy"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
var (
lock sync.RWMutex
// plugins is a map of plugin name to Plugin.
plugins = make(map[string]Plugin)
// eventHooks is a map of hook name to Hook. All hooks plugins
// must have a name.
eventHooks = make(map[string][]EventHook)
)
// SetupFunc is used to set up a plugin, or in other words,
// execute a directive. It will be called once per key for
// each server block it appears in.
type SetupFunc func(def *api.Definition, route *proxy.Route, rawConfig Config) error
// Config initialization options.
type Config map[string]interface{}
// Plugin defines basic methods for plugins
type Plugin struct {
Action SetupFunc
}
// RegisterPlugin plugs in plugin. All plugins should register
// themselves, even if they do not perform an action associated
// with a directive. It is important for the process to know
// which plugins are available.
//
// The plugin MUST have a name: lower case and one word.
// If this plugin has an action, it must be the name of
// the directive that invokes it. A name is always required
// and must be unique for the server type.
func RegisterPlugin(name string, plugin Plugin) error {
lock.Lock()
defer lock.Unlock()
if name == "" {
return errors.New("plugin must have a name")
}
if _, dup := plugins[name]; dup {
return fmt.Errorf("plugin named %s already registered", name)
}
plugins[name] = plugin
return nil
}
// EventHook is a type which holds information about a startup hook plugin.
type EventHook func(event interface{}) error
// RegisterEventHook plugs in hook. All the hooks should register themselves
// and they must have a name.
func RegisterEventHook(name string, hook EventHook) error {
log.WithField("event_name", name).Debug("Event registered")
lock.Lock()
defer lock.Unlock()
if name == "" {
return errors.New("event hook must have a name")
}
if hooks, dup := eventHooks[name]; dup {
eventHooks[name] = append(hooks, hook)
} else {
eventHooks[name] = append([]EventHook{}, hook)
}
return nil
}
// EmitEvent executes the different hooks passing the EventType as an
// argument. This is a blocking function. Hook developers should
// use 'go' keyword if they don't want to block Janus.
func EmitEvent(name string, event interface{}) error {
log.WithField("event_name", name).Debug("Event triggered")
hooks, found := eventHooks[name]
if !found {
return errors.New("Plugin not found")
}
for _, hook := range hooks {
err := hook(event)
if err != nil {
log.WithError(err).WithField("event_name", name).Warn("an error occurred when an event was triggered")
}
}
return nil
}
// DirectiveAction gets the action for a plugin
func DirectiveAction(name string) (SetupFunc, error) {
if plugin, ok := plugins[name]; ok {
return plugin.Action, nil
}
return nil, fmt.Errorf("no action found for plugin '%s' (missing a plugin?)", name)
}
// Decode decodes a map string interface into a struct
// for some reasons mapstructure.Decode() gives empty arrays for all resulting config fields
// this is quick workaround hack t make it work
// FIXME: investigate and fix mapstructure.Decode() behaviour and remove this dirty hack
func Decode(rawConfig map[string]interface{}, obj interface{}) error {
valJSON, err := json.Marshal(rawConfig)
if nil != err {
return err
}
err = json.Unmarshal(valJSON, obj)
if nil != err {
return err
}
return nil
}