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