package process import ( "context" "errors" "time" "github.com/grafana/grafana/pkg/plugins" ) type Service struct{} func ProvideService() *Service { return &Service{} } func (*Service) Start(ctx context.Context, p *plugins.Plugin) error { if !p.IsManaged() || !p.Backend || p.SignatureError != nil { return nil } if err := startPluginAndRestartKilledProcesses(ctx, p); err != nil { return err } p.Logger().Debug("Successfully started backend plugin process") return nil } func (*Service) Stop(ctx context.Context, p *plugins.Plugin) error { p.Logger().Debug("Stopping plugin process") if err := p.Decommission(); err != nil { return err } if err := p.Stop(ctx); err != nil { return err } return nil } func startPluginAndRestartKilledProcesses(ctx context.Context, p *plugins.Plugin) error { if err := p.Start(ctx); err != nil { return err } if p.IsCorePlugin() { return nil } go func(ctx context.Context, p *plugins.Plugin) { if err := restartKilledProcess(ctx, p); err != nil { p.Logger().Error("Attempt to restart killed plugin process failed", "error", err) } }(ctx, p) return nil } func restartKilledProcess(ctx context.Context, p *plugins.Plugin) error { ticker := time.NewTicker(time.Second * 1) for { select { case <-ctx.Done(): if err := ctx.Err(); err != nil && !errors.Is(err, context.Canceled) { return err } return p.Stop(ctx) case <-ticker.C: if p.IsDecommissioned() { p.Logger().Debug("Plugin decommissioned") return nil } if !p.Exited() { continue } p.Logger().Debug("Restarting plugin") if err := p.Start(ctx); err != nil { p.Logger().Error("Failed to restart plugin", "error", err) continue } p.Logger().Debug("Plugin restarted") } } }