krakend-ce
/
plugin.go
274 строки · 6.7 Кб
1package krakend
2
3import (
4"context"
5"fmt"
6"os"
7"path/filepath"
8"regexp"
9"strings"
10"time"
11
12cmd "github.com/krakendio/krakend-cobra/v2"
13"github.com/luraproject/lura/v2/logging"
14proxy "github.com/luraproject/lura/v2/proxy/plugin"
15client "github.com/luraproject/lura/v2/transport/http/client/plugin"
16server "github.com/luraproject/lura/v2/transport/http/server/plugin"
17"github.com/spf13/cobra"
18)
19
20// LoadPlugins loads and registers the plugins so they can be used if enabled at the configuration
21func LoadPlugins(folder, pattern string, logger logging.Logger) {
22LoadPluginsWithContext(context.Background(), folder, pattern, logger)
23}
24
25func LoadPluginsWithContext(ctx context.Context, folder, pattern string, logger logging.Logger) {
26logger.Debug("[SERVICE: Plugin Loader] Starting loading process")
27
28n, err := client.LoadWithLogger(
29folder,
30pattern,
31client.RegisterClient,
32logger,
33)
34logPluginLoaderErrors(logger, "[SERVICE: Executor Plugin]", n, err)
35
36n, err = server.LoadWithLogger(
37folder,
38pattern,
39server.RegisterHandler,
40logger,
41)
42logPluginLoaderErrors(logger, "[SERVICE: Handler Plugin]", n, err)
43
44n, err = proxy.LoadWithLoggerAndContext(
45ctx,
46folder,
47pattern,
48proxy.RegisterModifier,
49logger,
50)
51logPluginLoaderErrors(logger, "[SERVICE: Modifier Plugin]", n, err)
52
53logger.Debug("[SERVICE: Plugin Loader] Loading process completed")
54}
55
56func logPluginLoaderErrors(logger logging.Logger, tag string, n int, err error) {
57if err != nil {
58if mErrs, ok := err.(pluginLoaderErr); ok {
59for _, err := range mErrs.Errs() {
60logger.Debug(tag, err.Error())
61}
62} else {
63logger.Debug(tag, err.Error())
64}
65}
66if n > 0 {
67logger.Info(tag, "Total plugins loaded:", n)
68}
69}
70
71type pluginLoader struct{}
72
73func (pluginLoader) Load(folder, pattern string, logger logging.Logger) {
74LoadPlugins(folder, pattern, logger)
75}
76
77func (pluginLoader) LoadWithContext(ctx context.Context, folder, pattern string, logger logging.Logger) {
78LoadPluginsWithContext(ctx, folder, pattern, logger)
79}
80
81type pluginLoaderErr interface {
82Errs() []error
83}
84
85var (
86serverExpected bool
87clientExpected bool
88modifierExpected bool
89
90testPluginCmd = &cobra.Command{
91Use: "test-plugin [flags] [artifacts]",
92Short: "Tests that one or more plugins are loadable into KrakenD.",
93Run: testPluginFunc,
94Example: "krakend test-plugin -scm ./plugins/my_plugin.so ./plugins/my_other_plugin.so",
95}
96
97serverExpectedFlag cmd.FlagBuilder
98clientExpectedFlag cmd.FlagBuilder
99modifierExpectedFlag cmd.FlagBuilder
100
101reLogErrorPlugins = regexp.MustCompile(`(?m)plugin \#\d+ \(.*\): (.*)`)
102)
103
104func init() {
105serverExpectedFlag = cmd.BoolFlagBuilder(&serverExpected, "server", "s", false, "The artifact should contain a Server Plugin.")
106clientExpectedFlag = cmd.BoolFlagBuilder(&clientExpected, "client", "c", false, "The artifact should contain a Client Plugin.")
107modifierExpectedFlag = cmd.BoolFlagBuilder(&modifierExpected, "modifier", "m", false, "The artifact should contain a Req/Resp Modifier Plugin.")
108}
109
110func NewTestPluginCmd() cmd.Command {
111return cmd.NewCommand(testPluginCmd, serverExpectedFlag, clientExpectedFlag, modifierExpectedFlag)
112}
113
114func testPluginFunc(ccmd *cobra.Command, args []string) {
115if len(args) == 0 {
116ccmd.Println("At least one plugin is required.")
117os.Exit(1)
118}
119if !serverExpected && !clientExpected && !modifierExpected {
120ccmd.Println("You must declare the expected type of the plugin.")
121os.Exit(1)
122}
123
124start := time.Now()
125
126ctx, cancel := context.WithCancel(context.Background())
127
128var failed int
129globalOK := true
130for _, pluginPath := range args {
131f, err := os.Open(pluginPath)
132if os.IsNotExist(err) {
133ccmd.Println(fmt.Sprintf("[KO] Unable to open the plugin %s.", pluginPath))
134failed++
135globalOK = false
136continue
137}
138f.Close()
139
140name := filepath.Base(pluginPath)
141folder := filepath.Dir(pluginPath)
142ok := true
143
144if serverExpected {
145ok = checkHandlerPlugin(ccmd, folder, name) && ok
146}
147
148if modifierExpected {
149ok = checkModifierPlugin(ctx, ccmd, folder, name) && ok
150}
151
152if clientExpected {
153ok = checkClientPlugin(ccmd, folder, name) && ok
154}
155
156if !ok {
157failed++
158}
159
160globalOK = globalOK && ok
161}
162
163cancel()
164
165if !globalOK {
166ccmd.Println(fmt.Sprintf("[KO] %d tested plugin(s) in %s.\n%d plugin(s) failed.", len(args), time.Since(start), failed))
167os.Exit(1)
168}
169
170ccmd.Println(fmt.Sprintf("[OK] %d tested plugin(s) in %s", len(args), time.Since(start)))
171}
172
173func checkClientPlugin(ccmd *cobra.Command, folder, name string) bool {
174_, err := client.LoadWithLogger(
175folder,
176name,
177client.RegisterClient,
178logging.NoOp,
179)
180if err == nil {
181ccmd.Println(fmt.Sprintf("[OK] CLIENT\t%s", name))
182return true
183}
184
185var msg string
186if mErrs, ok := err.(pluginLoaderErr); ok {
187for _, err := range mErrs.Errs() {
188msg += err.Error()
189}
190} else {
191msg = err.Error()
192}
193
194if strings.Contains(msg, "symbol ClientRegisterer not found") {
195ccmd.Println(fmt.Sprintf("[KO] CLIENT\t%s: The plugin does not contain a ClientRegisterer.", name))
196return false
197}
198
199for _, match := range reLogErrorPlugins.FindAllStringSubmatch(msg, -1) {
200msg = match[1]
201}
202
203ccmd.Println(fmt.Sprintf("[KO] CLIENT\t%s: %s", name, msg))
204return false
205}
206
207func checkHandlerPlugin(ccmd *cobra.Command, folder, name string) bool {
208_, err := server.LoadWithLogger(
209folder,
210name,
211server.RegisterHandler,
212logging.NoOp,
213)
214if err == nil {
215ccmd.Println(fmt.Sprintf("[OK] SERVER\t%s", name))
216return true
217}
218
219var msg string
220if mErrs, ok := err.(pluginLoaderErr); ok {
221for _, err := range mErrs.Errs() {
222msg += err.Error()
223}
224} else {
225msg = err.Error()
226}
227
228if strings.Contains(msg, "symbol HandlerRegisterer not found") {
229ccmd.Println(fmt.Sprintf("[KO] SERVER\t%s: The plugin does not contain a HandlerRegisterer.", name))
230return false
231}
232
233for _, match := range reLogErrorPlugins.FindAllStringSubmatch(msg, -1) {
234msg = match[1]
235}
236
237ccmd.Println(fmt.Sprintf("[KO] SERVER\t%s: %s", name, msg))
238return false
239}
240
241func checkModifierPlugin(ctx context.Context, ccmd *cobra.Command, folder, name string) bool {
242_, err := proxy.LoadWithLoggerAndContext(
243ctx,
244folder,
245name,
246proxy.RegisterModifier,
247logging.NoOp,
248)
249if err == nil {
250ccmd.Println(fmt.Sprintf("[OK] MODIFIER\t%s", name))
251return true
252}
253
254var msg string
255if mErrs, ok := err.(pluginLoaderErr); ok {
256for _, err := range mErrs.Errs() {
257msg += err.Error()
258}
259} else {
260msg = err.Error()
261}
262
263if strings.Contains(msg, "symbol ModifierRegisterer not found") {
264ccmd.Println(fmt.Sprintf("[KO] MODIFIER\t%s: The plugin does not contain a ModifierRegisterer.", name))
265return false
266}
267
268for _, match := range reLogErrorPlugins.FindAllStringSubmatch(msg, -1) {
269msg = match[1]
270}
271
272ccmd.Println(fmt.Sprintf("[KO] MODIFIER\t%s: %s", name, msg))
273return false
274}
275