1package manager
2
3import (
4	"bytes"
5	"context"
6	"net/http"
7	"net/http/httptest"
8	"os"
9	"path/filepath"
10	"sync"
11	"testing"
12	"time"
13
14	"github.com/grafana/grafana-plugin-sdk-go/backend"
15
16	"github.com/grafana/grafana/pkg/infra/fs"
17	"github.com/grafana/grafana/pkg/infra/log"
18	"github.com/grafana/grafana/pkg/plugins"
19	"github.com/grafana/grafana/pkg/plugins/backendplugin"
20	"github.com/grafana/grafana/pkg/services/sqlstore"
21	"github.com/grafana/grafana/pkg/setting"
22
23	"github.com/stretchr/testify/assert"
24	"github.com/stretchr/testify/require"
25	"gopkg.in/ini.v1"
26)
27
28const (
29	testPluginID = "test-plugin"
30)
31
32func TestPluginManager_init(t *testing.T) {
33	t.Run("Plugin folder will be created if not exists", func(t *testing.T) {
34		testDir := "plugin-test-dir"
35
36		exists, err := fs.Exists(testDir)
37		require.NoError(t, err)
38		assert.False(t, exists)
39
40		pm := createManager(t, func(pm *PluginManager) {
41			pm.cfg.PluginsPath = testDir
42		})
43
44		err = pm.init()
45		require.NoError(t, err)
46
47		exists, err = fs.Exists(testDir)
48		require.NoError(t, err)
49		assert.True(t, exists)
50
51		t.Cleanup(func() {
52			err = os.Remove(testDir)
53			require.NoError(t, err)
54		})
55	})
56}
57
58func TestPluginManager_loadPlugins(t *testing.T) {
59	t.Run("Managed backend plugin", func(t *testing.T) {
60		p, pc := createPlugin(testPluginID, "", plugins.External, true, true)
61
62		loader := &fakeLoader{
63			mockedLoadedPlugins: []*plugins.Plugin{p},
64		}
65
66		pm := createManager(t, func(pm *PluginManager) {
67			pm.pluginLoader = loader
68		})
69		err := pm.loadPlugins("test/path")
70		require.NoError(t, err)
71
72		assert.Equal(t, 1, pc.startCount)
73		assert.Equal(t, 0, pc.stopCount)
74		assert.False(t, pc.exited)
75		assert.False(t, pc.decommissioned)
76
77		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
78		assert.True(t, exists)
79		assert.Equal(t, p.ToDTO(), testPlugin)
80		assert.Len(t, pm.Plugins(context.Background()), 1)
81
82		verifyNoPluginErrors(t, pm)
83	})
84
85	t.Run("Unmanaged backend plugin", func(t *testing.T) {
86		p, pc := createPlugin(testPluginID, "", plugins.External, false, true)
87
88		loader := &fakeLoader{
89			mockedLoadedPlugins: []*plugins.Plugin{p},
90		}
91
92		pm := createManager(t, func(pm *PluginManager) {
93			pm.pluginLoader = loader
94		})
95		err := pm.loadPlugins("test/path")
96		require.NoError(t, err)
97
98		assert.Equal(t, 0, pc.startCount)
99		assert.Equal(t, 0, pc.stopCount)
100		assert.False(t, pc.exited)
101		assert.False(t, pc.decommissioned)
102
103		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
104		assert.True(t, exists)
105		assert.Equal(t, p.ToDTO(), testPlugin)
106		assert.Len(t, pm.Plugins(context.Background()), 1)
107
108		verifyNoPluginErrors(t, pm)
109	})
110
111	t.Run("Managed non-backend plugin", func(t *testing.T) {
112		p, pc := createPlugin(testPluginID, "", plugins.External, false, true)
113
114		loader := &fakeLoader{
115			mockedLoadedPlugins: []*plugins.Plugin{p},
116		}
117
118		pm := createManager(t, func(pm *PluginManager) {
119			pm.pluginLoader = loader
120		})
121		err := pm.loadPlugins("test/path")
122		require.NoError(t, err)
123
124		assert.Equal(t, 0, pc.startCount)
125		assert.Equal(t, 0, pc.stopCount)
126		assert.False(t, pc.exited)
127		assert.False(t, pc.decommissioned)
128
129		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
130		assert.True(t, exists)
131		assert.Equal(t, p.ToDTO(), testPlugin)
132		assert.Len(t, pm.Plugins(context.Background()), 1)
133
134		verifyNoPluginErrors(t, pm)
135	})
136
137	t.Run("Unmanaged non-backend plugin", func(t *testing.T) {
138		p, pc := createPlugin(testPluginID, "", plugins.External, false, false)
139
140		loader := &fakeLoader{
141			mockedLoadedPlugins: []*plugins.Plugin{p},
142		}
143
144		pm := createManager(t, func(pm *PluginManager) {
145			pm.pluginLoader = loader
146		})
147		err := pm.loadPlugins("test/path")
148		require.NoError(t, err)
149
150		assert.Equal(t, 0, pc.startCount)
151		assert.Equal(t, 0, pc.stopCount)
152		assert.False(t, pc.exited)
153		assert.False(t, pc.decommissioned)
154
155		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
156		assert.True(t, exists)
157		assert.Equal(t, p.ToDTO(), testPlugin)
158		assert.Len(t, pm.Plugins(context.Background()), 1)
159
160		verifyNoPluginErrors(t, pm)
161	})
162}
163
164func TestPluginManager_Installer(t *testing.T) {
165	t.Run("Install", func(t *testing.T) {
166		p, pc := createPlugin(testPluginID, "1.0.0", plugins.External, true, true)
167
168		l := &fakeLoader{
169			mockedLoadedPlugins: []*plugins.Plugin{p},
170		}
171
172		i := &fakePluginInstaller{}
173		pm := createManager(t, func(pm *PluginManager) {
174			pm.pluginInstaller = i
175			pm.pluginLoader = l
176		})
177
178		err := pm.Add(context.Background(), testPluginID, "1.0.0", plugins.AddOpts{})
179		require.NoError(t, err)
180
181		assert.Equal(t, 1, i.installCount)
182		assert.Equal(t, 0, i.uninstallCount)
183
184		verifyNoPluginErrors(t, pm)
185
186		assert.Len(t, pm.Routes(), 1)
187		assert.Equal(t, p.ID, pm.Routes()[0].PluginID)
188		assert.Equal(t, p.PluginDir, pm.Routes()[0].Directory)
189
190		assert.Equal(t, 1, pc.startCount)
191		assert.Equal(t, 0, pc.stopCount)
192		assert.False(t, pc.exited)
193		assert.False(t, pc.decommissioned)
194
195		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
196		assert.True(t, exists)
197		assert.Equal(t, p.ToDTO(), testPlugin)
198		assert.Len(t, pm.Plugins(context.Background()), 1)
199
200		t.Run("Won't install if already installed", func(t *testing.T) {
201			err := pm.Add(context.Background(), testPluginID, "1.0.0", plugins.AddOpts{})
202			assert.Equal(t, plugins.DuplicateError{
203				PluginID:          p.ID,
204				ExistingPluginDir: p.PluginDir,
205			}, err)
206		})
207
208		t.Run("Update", func(t *testing.T) {
209			p, pc := createPlugin(testPluginID, "1.2.0", plugins.External, true, true)
210
211			l := &fakeLoader{
212				mockedLoadedPlugins: []*plugins.Plugin{p},
213			}
214			pm.pluginLoader = l
215
216			err = pm.Add(context.Background(), testPluginID, "1.2.0", plugins.AddOpts{})
217			assert.NoError(t, err)
218
219			assert.Equal(t, 2, i.installCount)
220			assert.Equal(t, 1, i.uninstallCount)
221
222			assert.Equal(t, 1, pc.startCount)
223			assert.Equal(t, 0, pc.stopCount)
224			assert.False(t, pc.exited)
225			assert.False(t, pc.decommissioned)
226
227			testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
228			assert.True(t, exists)
229			assert.Equal(t, p.ToDTO(), testPlugin)
230			assert.Len(t, pm.Plugins(context.Background()), 1)
231		})
232
233		t.Run("Uninstall", func(t *testing.T) {
234			err := pm.Remove(context.Background(), p.ID)
235			require.NoError(t, err)
236
237			assert.Equal(t, 2, i.installCount)
238			assert.Equal(t, 2, i.uninstallCount)
239
240			p, exists := pm.Plugin(context.Background(), p.ID)
241			assert.False(t, exists)
242			assert.Equal(t, plugins.PluginDTO{}, p)
243			assert.Len(t, pm.Routes(), 0)
244
245			t.Run("Won't uninstall if not installed", func(t *testing.T) {
246				err := pm.Remove(context.Background(), p.ID)
247				require.Equal(t, plugins.ErrPluginNotInstalled, err)
248			})
249		})
250	})
251
252	t.Run("Can't update core plugin", func(t *testing.T) {
253		p, pc := createPlugin(testPluginID, "", plugins.Core, true, true)
254
255		loader := &fakeLoader{
256			mockedLoadedPlugins: []*plugins.Plugin{p},
257		}
258
259		pm := createManager(t, func(pm *PluginManager) {
260			pm.pluginLoader = loader
261		})
262		err := pm.loadPlugins("test/path")
263		require.NoError(t, err)
264
265		assert.Equal(t, 1, pc.startCount)
266		assert.Equal(t, 0, pc.stopCount)
267		assert.False(t, pc.exited)
268		assert.False(t, pc.decommissioned)
269
270		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
271		assert.True(t, exists)
272		assert.Equal(t, p.ToDTO(), testPlugin)
273		assert.Len(t, pm.Plugins(context.Background()), 1)
274
275		verifyNoPluginErrors(t, pm)
276
277		err = pm.Add(context.Background(), testPluginID, "", plugins.AddOpts{})
278		assert.Equal(t, plugins.ErrInstallCorePlugin, err)
279
280		t.Run("Can't uninstall core plugin", func(t *testing.T) {
281			err := pm.Remove(context.Background(), p.ID)
282			require.Equal(t, plugins.ErrUninstallCorePlugin, err)
283		})
284	})
285
286	t.Run("Can't update bundled plugin", func(t *testing.T) {
287		p, pc := createPlugin(testPluginID, "", plugins.Bundled, true, true)
288
289		loader := &fakeLoader{
290			mockedLoadedPlugins: []*plugins.Plugin{p},
291		}
292
293		pm := createManager(t, func(pm *PluginManager) {
294			pm.pluginLoader = loader
295		})
296		err := pm.loadPlugins("test/path")
297		require.NoError(t, err)
298
299		assert.Equal(t, 1, pc.startCount)
300		assert.Equal(t, 0, pc.stopCount)
301		assert.False(t, pc.exited)
302		assert.False(t, pc.decommissioned)
303
304		testPlugin, exists := pm.Plugin(context.Background(), testPluginID)
305		assert.True(t, exists)
306		assert.Equal(t, p.ToDTO(), testPlugin)
307		assert.Len(t, pm.Plugins(context.Background()), 1)
308
309		verifyNoPluginErrors(t, pm)
310
311		err = pm.Add(context.Background(), testPluginID, "", plugins.AddOpts{})
312		assert.Equal(t, plugins.ErrInstallCorePlugin, err)
313
314		t.Run("Can't uninstall bundled plugin", func(t *testing.T) {
315			err := pm.Remove(context.Background(), p.ID)
316			require.Equal(t, plugins.ErrUninstallCorePlugin, err)
317		})
318	})
319}
320
321func TestPluginManager_lifecycle_managed(t *testing.T) {
322	newScenario(t, true, func(t *testing.T, ctx *managerScenarioCtx) {
323		t.Run("Managed plugin scenario", func(t *testing.T) {
324			t.Run("Should be able to register plugin", func(t *testing.T) {
325				err := ctx.manager.registerAndStart(context.Background(), ctx.plugin)
326				require.NoError(t, err)
327				require.NotNil(t, ctx.plugin)
328				require.Equal(t, testPluginID, ctx.plugin.ID)
329				require.Equal(t, 1, ctx.pluginClient.startCount)
330				testPlugin, exists := ctx.manager.Plugin(context.Background(), testPluginID)
331				assert.True(t, exists)
332				require.NotNil(t, testPlugin)
333
334				t.Run("Should not be able to register an already registered plugin", func(t *testing.T) {
335					err := ctx.manager.registerAndStart(context.Background(), ctx.plugin)
336					require.Equal(t, 1, ctx.pluginClient.startCount)
337					require.Error(t, err)
338				})
339
340				t.Run("When manager runs should start and stop plugin", func(t *testing.T) {
341					pCtx := context.Background()
342					cCtx, cancel := context.WithCancel(pCtx)
343					var wg sync.WaitGroup
344					wg.Add(1)
345					var runErr error
346					go func() {
347						runErr = ctx.manager.Run(cCtx)
348						wg.Done()
349					}()
350					time.Sleep(time.Millisecond)
351					cancel()
352					wg.Wait()
353					require.Equal(t, context.Canceled, runErr)
354					require.Equal(t, 1, ctx.pluginClient.startCount)
355					require.Equal(t, 1, ctx.pluginClient.stopCount)
356				})
357
358				t.Run("When manager runs should restart plugin process when killed", func(t *testing.T) {
359					ctx.pluginClient.stopCount = 0
360					ctx.pluginClient.startCount = 0
361					pCtx := context.Background()
362					cCtx, cancel := context.WithCancel(pCtx)
363					var wgRun sync.WaitGroup
364					wgRun.Add(1)
365					var runErr error
366					go func() {
367						runErr = ctx.manager.Run(cCtx)
368						wgRun.Done()
369					}()
370
371					time.Sleep(time.Millisecond)
372
373					var wgKill sync.WaitGroup
374					wgKill.Add(1)
375					go func() {
376						ctx.pluginClient.kill()
377						for {
378							if !ctx.plugin.Exited() {
379								break
380							}
381						}
382						cancel()
383						wgKill.Done()
384					}()
385					wgKill.Wait()
386					wgRun.Wait()
387					require.Equal(t, context.Canceled, runErr)
388					require.Equal(t, 1, ctx.pluginClient.stopCount)
389					require.Equal(t, 1, ctx.pluginClient.startCount)
390				})
391
392				t.Run("Unimplemented handlers", func(t *testing.T) {
393					t.Run("Collect metrics should return method not implemented error", func(t *testing.T) {
394						_, err = ctx.manager.CollectMetrics(context.Background(), testPluginID)
395						require.Equal(t, backendplugin.ErrMethodNotImplemented, err)
396					})
397
398					t.Run("Check health should return method not implemented error", func(t *testing.T) {
399						_, err = ctx.manager.CheckHealth(context.Background(), &backend.CheckHealthRequest{PluginContext: backend.PluginContext{PluginID: testPluginID}})
400						require.Equal(t, backendplugin.ErrMethodNotImplemented, err)
401					})
402
403					t.Run("Call resource should return method not implemented error", func(t *testing.T) {
404						req, err := http.NewRequest(http.MethodGet, "/test", bytes.NewReader([]byte{}))
405						require.NoError(t, err)
406						w := httptest.NewRecorder()
407						err = ctx.manager.callResourceInternal(w, req, backend.PluginContext{PluginID: testPluginID})
408						require.Equal(t, backendplugin.ErrMethodNotImplemented, err)
409					})
410				})
411
412				t.Run("Implemented handlers", func(t *testing.T) {
413					t.Run("Collect metrics should return expected result", func(t *testing.T) {
414						ctx.pluginClient.CollectMetricsHandlerFunc = func(ctx context.Context) (*backend.CollectMetricsResult, error) {
415							return &backend.CollectMetricsResult{
416								PrometheusMetrics: []byte("hello"),
417							}, nil
418						}
419
420						res, err := ctx.manager.CollectMetrics(context.Background(), testPluginID)
421						require.NoError(t, err)
422						require.NotNil(t, res)
423						require.Equal(t, "hello", string(res.PrometheusMetrics))
424					})
425
426					t.Run("Check health should return expected result", func(t *testing.T) {
427						json := []byte(`{
428							"key": "value"
429						}`)
430						ctx.pluginClient.CheckHealthHandlerFunc = func(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
431							return &backend.CheckHealthResult{
432								Status:      backend.HealthStatusOk,
433								Message:     "All good",
434								JSONDetails: json,
435							}, nil
436						}
437
438						res, err := ctx.manager.CheckHealth(context.Background(), &backend.CheckHealthRequest{PluginContext: backend.PluginContext{PluginID: testPluginID}})
439						require.NoError(t, err)
440						require.NotNil(t, res)
441						require.Equal(t, backend.HealthStatusOk, res.Status)
442						require.Equal(t, "All good", res.Message)
443						require.Equal(t, json, res.JSONDetails)
444					})
445
446					t.Run("Call resource should return expected response", func(t *testing.T) {
447						ctx.pluginClient.CallResourceHandlerFunc = func(ctx context.Context,
448							req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
449							return sender.Send(&backend.CallResourceResponse{
450								Status:  http.StatusOK,
451								Headers: map[string][]string{},
452							})
453						}
454
455						req, err := http.NewRequest(http.MethodGet, "/test", bytes.NewReader([]byte{}))
456						require.NoError(t, err)
457						w := httptest.NewRecorder()
458						err = ctx.manager.callResourceInternal(w, req, backend.PluginContext{PluginID: testPluginID})
459						require.NoError(t, err)
460						for {
461							if w.Flushed {
462								break
463							}
464						}
465						require.Equal(t, http.StatusOK, w.Code)
466						require.Equal(t, "sandbox", w.Header().Get("Content-Security-Policy"))
467					})
468				})
469			})
470		})
471	})
472}
473
474func TestPluginManager_lifecycle_unmanaged(t *testing.T) {
475	newScenario(t, false, func(t *testing.T, ctx *managerScenarioCtx) {
476		t.Run("Unmanaged plugin scenario", func(t *testing.T) {
477			t.Run("Should be able to register plugin", func(t *testing.T) {
478				err := ctx.manager.registerAndStart(context.Background(), ctx.plugin)
479				require.NoError(t, err)
480				require.True(t, ctx.manager.isRegistered(testPluginID))
481				require.False(t, ctx.pluginClient.managed)
482
483				t.Run("When manager runs should not start plugin", func(t *testing.T) {
484					pCtx := context.Background()
485					cCtx, cancel := context.WithCancel(pCtx)
486					var wg sync.WaitGroup
487					wg.Add(1)
488					var runErr error
489					go func() {
490						runErr = ctx.manager.Run(cCtx)
491						wg.Done()
492					}()
493					go func() {
494						cancel()
495					}()
496					wg.Wait()
497					require.Equal(t, context.Canceled, runErr)
498					require.Equal(t, 0, ctx.pluginClient.startCount)
499					require.Equal(t, 1, ctx.pluginClient.stopCount)
500					require.True(t, ctx.plugin.Exited())
501				})
502
503				t.Run("Should be not be able to start unmanaged plugin", func(t *testing.T) {
504					pCtx := context.Background()
505					cCtx, cancel := context.WithCancel(pCtx)
506					defer cancel()
507					err := ctx.manager.start(cCtx, ctx.plugin)
508					require.Nil(t, err)
509					require.Equal(t, 0, ctx.pluginClient.startCount)
510					require.True(t, ctx.plugin.Exited())
511				})
512			})
513		})
514	})
515}
516
517func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
518	t.Helper()
519
520	staticRootPath, err := filepath.Abs("../../../public/")
521	require.NoError(t, err)
522
523	cfg := &setting.Cfg{
524		Raw:            ini.Empty(),
525		Env:            setting.Prod,
526		StaticRootPath: staticRootPath,
527	}
528
529	requestValidator := &testPluginRequestValidator{}
530	loader := &fakeLoader{}
531	pm := newManager(cfg, requestValidator, loader, &sqlstore.SQLStore{})
532
533	for _, cb := range cbs {
534		cb(pm)
535	}
536
537	return pm
538}
539
540func createPlugin(pluginID, version string, class plugins.Class, managed, backend bool) (*plugins.Plugin, *fakePluginClient) {
541	p := &plugins.Plugin{
542		Class: class,
543		JSONData: plugins.JSONData{
544			ID:      pluginID,
545			Type:    plugins.DataSource,
546			Backend: backend,
547			Info: plugins.Info{
548				Version: version,
549			},
550		},
551	}
552
553	logger := fakeLogger{}
554
555	p.SetLogger(logger)
556
557	pc := &fakePluginClient{
558		pluginID: pluginID,
559		logger:   logger,
560		managed:  managed,
561	}
562
563	p.RegisterClient(pc)
564
565	return p, pc
566}
567
568type managerScenarioCtx struct {
569	manager      *PluginManager
570	plugin       *plugins.Plugin
571	pluginClient *fakePluginClient
572}
573
574func newScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *managerScenarioCtx)) {
575	t.Helper()
576	cfg := setting.NewCfg()
577	cfg.AWSAllowedAuthProviders = []string{"keys", "credentials"}
578	cfg.AWSAssumeRoleEnabled = true
579
580	cfg.Azure.ManagedIdentityEnabled = true
581	cfg.Azure.Cloud = "AzureCloud"
582	cfg.Azure.ManagedIdentityClientId = "client-id"
583
584	staticRootPath, err := filepath.Abs("../../../public")
585	require.NoError(t, err)
586	cfg.StaticRootPath = staticRootPath
587
588	requestValidator := &testPluginRequestValidator{}
589	loader := &fakeLoader{}
590	manager := newManager(cfg, requestValidator, loader, nil)
591	manager.pluginLoader = loader
592	ctx := &managerScenarioCtx{
593		manager: manager,
594	}
595
596	ctx.plugin, ctx.pluginClient = createPlugin(testPluginID, "", plugins.Core, managed, true)
597
598	fn(t, ctx)
599}
600
601func verifyNoPluginErrors(t *testing.T, pm *PluginManager) {
602	for _, plugin := range pm.Plugins(context.Background()) {
603		assert.Nil(t, plugin.SignatureError)
604	}
605}
606
607type fakePluginInstaller struct {
608	plugins.Installer
609
610	installCount   int
611	uninstallCount int
612}
613
614func (f *fakePluginInstaller) Install(ctx context.Context, pluginID, version, pluginsDir, pluginZipURL, pluginRepoURL string) error {
615	f.installCount++
616	return nil
617}
618
619func (f *fakePluginInstaller) Uninstall(ctx context.Context, pluginPath string) error {
620	f.uninstallCount++
621	return nil
622}
623
624func (f *fakePluginInstaller) GetUpdateInfo(ctx context.Context, pluginID, version, pluginRepoURL string) (plugins.UpdateInfo, error) {
625	return plugins.UpdateInfo{}, nil
626}
627
628type fakeLoader struct {
629	mockedLoadedPlugins       []*plugins.Plugin
630	mockedFactoryLoadedPlugin *plugins.Plugin
631
632	loadedPaths []string
633
634	plugins.Loader
635}
636
637func (l *fakeLoader) Load(paths []string, ignore map[string]struct{}) ([]*plugins.Plugin, error) {
638	l.loadedPaths = append(l.loadedPaths, paths...)
639
640	return l.mockedLoadedPlugins, nil
641}
642
643func (l *fakeLoader) LoadWithFactory(path string, factory backendplugin.PluginFactoryFunc) (*plugins.Plugin, error) {
644	l.loadedPaths = append(l.loadedPaths, path)
645
646	return l.mockedFactoryLoadedPlugin, nil
647}
648
649type fakePluginClient struct {
650	pluginID       string
651	logger         log.Logger
652	startCount     int
653	stopCount      int
654	managed        bool
655	exited         bool
656	decommissioned bool
657	backend.CollectMetricsHandlerFunc
658	backend.CheckHealthHandlerFunc
659	backend.QueryDataHandlerFunc
660	backend.CallResourceHandlerFunc
661	mutex sync.RWMutex
662
663	backendplugin.Plugin
664}
665
666func (tp *fakePluginClient) PluginID() string {
667	return tp.pluginID
668}
669
670func (tp *fakePluginClient) Logger() log.Logger {
671	return tp.logger
672}
673
674func (tp *fakePluginClient) Start(ctx context.Context) error {
675	tp.mutex.Lock()
676	defer tp.mutex.Unlock()
677	tp.exited = false
678	tp.startCount++
679	return nil
680}
681
682func (tp *fakePluginClient) Stop(ctx context.Context) error {
683	tp.mutex.Lock()
684	defer tp.mutex.Unlock()
685	tp.stopCount++
686	tp.exited = true
687	return nil
688}
689
690func (tp *fakePluginClient) IsManaged() bool {
691	return tp.managed
692}
693
694func (tp *fakePluginClient) Exited() bool {
695	tp.mutex.RLock()
696	defer tp.mutex.RUnlock()
697	return tp.exited
698}
699
700func (tp *fakePluginClient) Decommission() error {
701	tp.mutex.Lock()
702	defer tp.mutex.Unlock()
703
704	tp.decommissioned = true
705
706	return nil
707}
708
709func (tp *fakePluginClient) IsDecommissioned() bool {
710	tp.mutex.RLock()
711	defer tp.mutex.RUnlock()
712	return tp.decommissioned
713}
714
715func (tp *fakePluginClient) kill() {
716	tp.mutex.Lock()
717	defer tp.mutex.Unlock()
718	tp.exited = true
719}
720
721func (tp *fakePluginClient) CollectMetrics(ctx context.Context) (*backend.CollectMetricsResult, error) {
722	if tp.CollectMetricsHandlerFunc != nil {
723		return tp.CollectMetricsHandlerFunc(ctx)
724	}
725
726	return nil, backendplugin.ErrMethodNotImplemented
727}
728
729func (tp *fakePluginClient) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
730	if tp.CheckHealthHandlerFunc != nil {
731		return tp.CheckHealthHandlerFunc(ctx, req)
732	}
733
734	return nil, backendplugin.ErrMethodNotImplemented
735}
736
737func (tp *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
738	if tp.QueryDataHandlerFunc != nil {
739		return tp.QueryDataHandlerFunc(ctx, req)
740	}
741
742	return nil, backendplugin.ErrMethodNotImplemented
743}
744
745func (tp *fakePluginClient) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
746	if tp.CallResourceHandlerFunc != nil {
747		return tp.CallResourceHandlerFunc(ctx, req, sender)
748	}
749
750	return backendplugin.ErrMethodNotImplemented
751}
752
753func (tp *fakePluginClient) SubscribeStream(ctx context.Context, request *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
754	return nil, backendplugin.ErrMethodNotImplemented
755}
756
757func (tp *fakePluginClient) PublishStream(ctx context.Context, request *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
758	return nil, backendplugin.ErrMethodNotImplemented
759}
760
761func (tp *fakePluginClient) RunStream(ctx context.Context, request *backend.RunStreamRequest, sender *backend.StreamSender) error {
762	return backendplugin.ErrMethodNotImplemented
763}
764
765type testPluginRequestValidator struct{}
766
767func (t *testPluginRequestValidator) Validate(string, *http.Request) error {
768	return nil
769}
770
771type fakeLogger struct {
772	log.Logger
773}
774
775func (tl fakeLogger) Info(msg string, ctx ...interface{}) {
776
777}
778
779func (tl fakeLogger) Debug(msg string, ctx ...interface{}) {
780
781}
782