func getPluginType(t string) (core.PluginType, error) { if ityp, err := strconv.Atoi(t); err == nil { return core.PluginType(ityp), nil } ityp, err := core.ToPluginType(t) if err != nil { return core.PluginType(-1), err } return ityp, nil }
func TestGetPluginContentTypes(t *testing.T) { Convey("RPC client errors", t, func() { proxy := ControlProxy{Client: mockClient{RpcErr: true}} _, _, err := proxy.GetPluginContentTypes("", core.PluginType(1), 2) Convey("So the error should be passed through", func() { So(err.Error(), ShouldResemble, rpcErr.Error()) }) }) Convey("control.GetPluginContentTypes returns an error", t, func() { reply := &rpc.GetPluginContentTypesReply{ AcceptedTypes: []string{"accept"}, ReturnedTypes: []string{"return"}, Error: "error", } proxy := ControlProxy{Client: mockClient{ContentTypeReply: reply}} _, _, err := proxy.GetPluginContentTypes("", core.PluginType(1), 2) Convey("So err should resemble 'error' ", func() { So(err.Error(), ShouldResemble, "error") }) }) Convey("control.GetPluginContentTypes returns successfully", t, func() { reply := &rpc.GetPluginContentTypesReply{ AcceptedTypes: []string{"accept"}, ReturnedTypes: []string{"return"}, } proxy := ControlProxy{Client: mockClient{ContentTypeReply: reply}} act, rct, err := proxy.GetPluginContentTypes("", core.PluginType(1), 2) Convey("So err should be nil", func() { So(err, ShouldBeNil) }) Convey("So accepted/returned types should not be nil", func() { So(act, ShouldNotBeNil) So(rct, ShouldNotBeNil) }) Convey("So accepted should contain 'accept'", func() { So(act, ShouldContain, "accept") }) Convey("So returned should contain 'return'", func() { So(rct, ShouldContain, "return") }) }) }
func (pc *ControlGRPCServer) GetPluginContentTypes(ctx context.Context, r *rpc.GetPluginContentTypesRequest) (*rpc.GetPluginContentTypesReply, error) { accepted, returned, err := pc.control.GetPluginContentTypes(r.Name, core.PluginType(int(r.PluginType)), int(r.Version)) reply := &rpc.GetPluginContentTypesReply{ AcceptedTypes: accepted, ReturnedTypes: returned, } if err != nil { reply.Error = err.Error() } return reply, nil }
// Empty handler acting as placeholder until implementation. This helps tests // pass to ensure registration works. func (r *runner) HandleGomitEvent(e gomit.Event) { switch v := e.Body.(type) { case *control_event.DeadAvailablePluginEvent: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), "aplugin": v.String, }).Warning("handling dead available plugin event") pool, err := r.availablePlugins.getPool(v.Key) if err != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", "aplugin": v.String, }).Error(err.Error()) return } if pool != nil { pool.Kill(v.Id, "plugin dead") } if pool.Eligible() { if pool.RestartCount() < MaxPluginRestartCount { e := r.restartPlugin(v.Key) if e != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", "aplugin": v.String, }).Error(e.Error()) return } pool.IncRestartCount() runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Name, "aplugin": v.Version, "restart_count": pool.RestartCount(), }).Warning("plugin restarted") r.emitter.Emit(&control_event.RestartedAvailablePluginEvent{ Id: v.Id, Name: v.Name, Version: v.Version, Key: v.Key, Type: v.Type, }) } else { r.emitter.Emit(&control_event.MaxPluginRestartsExceededEvent{ Id: v.Id, Name: v.Name, Version: v.Version, Key: v.Key, Type: v.Type, }) } } case *control_event.PluginUnsubscriptionEvent: runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.PluginName, "plugin-version": v.PluginVersion, "plugin-type": core.PluginType(v.PluginType).String(), }).Debug("handling plugin unsubscription event") err := r.handleUnsubscription(core.PluginType(v.PluginType).String(), v.PluginName, v.PluginVersion, v.TaskId) if err != nil { return } default: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), }).Info("Nothing to do for this event") } }
// HandleGomitEvent handles events emitted from control func (t *tribe) HandleGomitEvent(e gomit.Event) { logger := t.logger.WithFields(log.Fields{ "_block": "handle-gomit-event", }) switch v := e.Body.(type) { case *control_event.LoadPluginEvent: logger.WithFields(log.Fields{ "event": e.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Debugf("handling load plugin event") plugin := agreement.Plugin{ Name_: v.Name, Version_: v.Version, Type_: core.PluginType(v.Type), } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.PluginAgreement != nil { if ok, _ := m.PluginAgreement.Plugins.Contains(plugin); !ok { t.AddPlugin(m.PluginAgreement.Name, plugin) } } } case *control_event.UnloadPluginEvent: logger.WithFields(log.Fields{ "event": e.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Debugf("handling unload plugin event") plugin := agreement.Plugin{ Name_: v.Name, Version_: v.Version, Type_: core.PluginType(v.Type), } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.PluginAgreement != nil { if ok, _ := m.PluginAgreement.Plugins.Contains(plugin); ok { t.RemovePlugin(m.PluginAgreement.Name, plugin) } } } case *scheduler_event.TaskCreatedEvent: if v.Source != "tribe" { logger.WithFields(log.Fields{ "event": e.Namespace(), "task-id": v.TaskID, "task-start-on-create": v.StartOnCreate, }).Debugf("handling task create event") task := agreement.Task{ ID: v.TaskID, StartOnCreate: v.StartOnCreate, } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.TaskAgreements != nil { for n, a := range m.TaskAgreements { if ok, _ := a.Tasks.Contains(task); !ok { t.AddTask(n, task) } } } } } case *scheduler_event.TaskStoppedEvent: if v.Source != "tribe" { logger.WithFields(log.Fields{ "event": e.Namespace(), "task-id": v.TaskID, }).Debugf("handling task stop event") task := agreement.Task{ ID: v.TaskID, } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.TaskAgreements != nil { for n, a := range m.TaskAgreements { if ok, _ := a.Tasks.Contains(task); ok { t.StopTask(n, task) } } } } } case *scheduler_event.TaskStartedEvent: if v.Source != "tribe" { logger.WithFields(log.Fields{ "event": e.Namespace(), "task-id": v.TaskID, }).Debugf("handling task start event") task := agreement.Task{ ID: v.TaskID, } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.TaskAgreements != nil { for n, a := range m.TaskAgreements { if ok, _ := a.Tasks.Contains(task); ok { t.StartTask(n, task) } } } } } case *scheduler_event.TaskDeletedEvent: if v.Source != "tribe" { logger.WithFields(log.Fields{ "event": e.Namespace(), "task-id": v.TaskID, }).Debugf("handling task start event") task := agreement.Task{ ID: v.TaskID, } if m, ok := t.members[t.memberlist.LocalNode().Name]; ok { if m.TaskAgreements != nil { for n, a := range m.TaskAgreements { if ok, _ := a.Tasks.Contains(task); ok { t.RemoveTask(n, task) } } } } } } }
func TestPluginCatalog(t *testing.T) { ts := time.Now() c := New() // We need our own plugin manager to drop mock // loaded plugins into. Aribitrarily adding // plugins from the pm is no longer supported. tpm := newPluginManager() c.pluginManager = tpm lp1 := new(loadedPlugin) lp1.Meta = plugin.PluginMeta{Name: "test1", Version: 1, AcceptedContentTypes: []string{"a", "b", "c"}, ReturnedContentTypes: []string{"a", "b", "c"}, } lp1.Type = 0 lp1.State = "loaded" lp1.LoadedTime = ts tpm.loadedPlugins.add(lp1) lp2 := new(loadedPlugin) lp2.Meta = plugin.PluginMeta{Name: "test2", Version: 1} lp2.Type = 0 lp2.State = "loaded" lp2.LoadedTime = ts tpm.loadedPlugins.add(lp2) lp3 := new(loadedPlugin) lp3.Meta = plugin.PluginMeta{Name: "test3", Version: 1} lp3.Type = 0 lp3.State = "loaded" lp3.LoadedTime = ts tpm.loadedPlugins.add(lp3) lp4 := new(loadedPlugin) lp4.Meta = plugin.PluginMeta{Name: "test1", Version: 4, AcceptedContentTypes: []string{"d", "e", "f"}, ReturnedContentTypes: []string{"d", "e", "f"}, } lp4.Type = 0 lp4.State = "loaded" lp4.LoadedTime = ts tpm.loadedPlugins.add(lp4) lp5 := new(loadedPlugin) lp5.Meta = plugin.PluginMeta{Name: "test1", Version: 0, AcceptedContentTypes: []string{"d", "e", "f"}, ReturnedContentTypes: []string{"d", "e", "f"}, } lp5.Type = 0 lp5.State = "loaded" lp5.LoadedTime = ts tpm.loadedPlugins.add(lp5) pc := c.PluginCatalog() Convey("it returns a list of CatalogedPlugins (PluginCatalog)", t, func() { So(pc, ShouldHaveSameTypeAs, core.PluginCatalog{}) }) Convey("the loadedPlugins implement the interface CatalogedPlugin interface", t, func() { So(lp1.Name(), ShouldEqual, "test1") }) Convey("GetPluginContentTypes", t, func() { Convey("Given a plugin that exists", func() { act, ret, err := c.GetPluginContentTypes("test1", core.PluginType(0), 1) So(err, ShouldBeNil) So(act, ShouldResemble, []string{"a", "b", "c"}) So(ret, ShouldResemble, []string{"a", "b", "c"}) }) Convey("Given a plugin with a version that does NOT exist", func() { act, ret, err := c.GetPluginContentTypes("test1", core.PluginType(0), 5) So(err, ShouldNotBeNil) So(act, ShouldBeEmpty) So(ret, ShouldBeEmpty) }) Convey("Given a plugin where the version provided is 0", func() { act, ret, err := c.GetPluginContentTypes("test1", core.PluginType(0), 0) So(err, ShouldBeNil) So(act, ShouldResemble, []string{"d", "e", "f"}) So(ret, ShouldResemble, []string{"d", "e", "f"}) }) Convey("Given no plugins for the name and type", func() { act, ret, err := c.GetPluginContentTypes("test9", core.PluginType(0), 5) So(err, ShouldNotBeNil) So(act, ShouldBeEmpty) So(ret, ShouldBeEmpty) }) }) }
// LoadPlugin is the method for loading a plugin and // saving plugin into the LoadedPlugins array func (p *pluginManager) LoadPlugin(details *pluginDetails, emitter gomit.Emitter) (*loadedPlugin, serror.SnapError) { lPlugin := new(loadedPlugin) lPlugin.Details = details lPlugin.State = DetectedState pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "path": filepath.Base(lPlugin.Details.Exec), }).Info("plugin load called") ePlugin, err := plugin.NewExecutablePlugin(p.GenerateArgs(int(log.GetLevel())), path.Join(lPlugin.Details.ExecPath, lPlugin.Details.Exec)) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while creating executable plugin") return nil, serror.New(err) } pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "path": filepath.Base(lPlugin.Details.Exec), }).Debug(fmt.Sprintf("plugin load timeout set to %ds", p.pluginLoadTimeout)) resp, err := ePlugin.Run(time.Second * time.Duration(p.pluginLoadTimeout)) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error when starting plugin") return nil, serror.New(err) } ap, err := newAvailablePlugin(resp, emitter, ePlugin) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while creating available plugin") return nil, serror.New(err) } if resp.Meta.Unsecure { err = ap.client.Ping() } else { err = ap.client.SetKey() } if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while pinging the plugin") return nil, serror.New(err) } // Get the ConfigPolicy and add it to the loaded plugin c, ok := ap.client.(plugin.Plugin) if !ok { return nil, serror.New(errors.New("missing GetConfigPolicy function")) } cp, err := c.GetConfigPolicy() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "error": err.Error(), "plugin-name": ap.Name(), "plugin-version": ap.Version(), "plugin-id": ap.ID(), }).Error("error in getting config policy") return nil, serror.New(err) } lPlugin.ConfigPolicy = cp if resp.Type == plugin.CollectorPluginType { cfgNode := p.pluginConfig.getPluginConfigDataNode(core.PluginType(resp.Type), resp.Meta.Name, resp.Meta.Version) if lPlugin.ConfigPolicy != nil { // Get plugin config defaults defaults := cdata.NewNode() cpolicies := lPlugin.ConfigPolicy.GetAll() for _, cpolicy := range cpolicies { _, errs := cpolicy.AddDefaults(defaults.Table()) if len(errs.Errors()) > 0 { for _, err := range errs.Errors() { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "plugin-name": ap.Name(), "plugin-version": ap.Version(), "plugin-id": ap.ID(), }).Error(err.Error()) } return nil, serror.New(errors.New("error getting default config")) } } // Update config policy with defaults cfgNode.ReverseMerge(defaults) cp, err = c.GetConfigPolicy() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "error": err.Error(), "plugin-name": ap.Name(), "plugin-version": ap.Version(), "plugin-id": ap.ID(), }).Error("error in getting config policy") return nil, serror.New(err) } lPlugin.ConfigPolicy = cp } colClient := ap.client.(client.PluginCollectorClient) cfg := plugin.ConfigType{ ConfigDataNode: cfgNode, } metricTypes, err := colClient.GetMetricTypes(cfg) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "error": err.Error(), }).Error("error in getting metric types") return nil, serror.New(err) } // Add metric types to metric catalog for _, nmt := range metricTypes { // If the version is 0 default it to the plugin version // This honors the plugins explicit version but falls back // to the plugin version as default if nmt.Version() < 1 { // Since we have to override version we convert to a internal struct nmt = &metricType{ namespace: nmt.Namespace(), version: resp.Meta.Version, lastAdvertisedTime: nmt.LastAdvertisedTime(), config: nmt.Config(), data: nmt.Data(), tags: nmt.Tags(), description: nmt.Description(), unit: nmt.Unit(), } } // We quit and throw an error on bad metric versions (<1) // the is a safety catch otherwise the catalog will be corrupted if nmt.Version() < 1 { err := errors.New("Bad metric version from plugin") pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-name": resp.Meta.Name, "plugin-version": resp.Meta.Version, "plugin-type": resp.Meta.Type.String(), "plugin-path": filepath.Base(lPlugin.Details.ExecPath), "metric-namespace": nmt.Namespace(), "metric-version": nmt.Version(), "error": err.Error(), }).Error("received metric with bad version") return nil, serror.New(err) } //Add standard tags nmt = addStandardAndWorkflowTags(nmt, nil) if err := p.metricCatalog.AddLoadedMetricType(lPlugin, nmt); err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-name": resp.Meta.Name, "plugin-version": resp.Meta.Version, "plugin-type": resp.Meta.Type.String(), "plugin-path": filepath.Base(lPlugin.Details.ExecPath), "metric-namespace": nmt.Namespace(), "metric-version": nmt.Version(), "error": err.Error(), }).Error("error adding loaded metric type") return nil, serror.New(err) } } } // Added so clients can adequately clean up connections ap.client.Kill("Retrieved necessary plugin info") err = ePlugin.Kill() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while killing plugin executable plugin") return nil, serror.New(err) } if resp.State != plugin.PluginSuccess { e := fmt.Errorf("Plugin loading did not succeed: %s\n", resp.ErrorMessage) pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": e, "plugin response": resp.ErrorMessage, }).Error("load plugin error") return nil, serror.New(e) } lPlugin.Meta = resp.Meta lPlugin.Type = resp.Type lPlugin.Token = resp.Token lPlugin.LoadedTime = time.Now() lPlugin.State = LoadedState aErr := p.loadedPlugins.add(lPlugin) if aErr != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": aErr, }).Error("load plugin error while adding loaded plugin to load plugins collection") return nil, aErr } return lPlugin, nil }
// LoadPlugin is the method for loading a plugin and // saving plugin into the LoadedPlugins array func (p *pluginManager) LoadPlugin(details *pluginDetails, emitter gomit.Emitter) (*loadedPlugin, serror.SnapError) { lPlugin := new(loadedPlugin) lPlugin.Details = details lPlugin.State = DetectedState pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "path": filepath.Base(lPlugin.Details.Exec), }).Info("plugin load called") ePlugin, err := plugin.NewExecutablePlugin(p.GenerateArgs(lPlugin.Details.Exec), path.Join(lPlugin.Details.ExecPath, lPlugin.Details.Exec)) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while creating executable plugin") return nil, serror.New(err) } err = ePlugin.Start() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while starting plugin") return nil, serror.New(err) } var resp *plugin.Response resp, err = ePlugin.WaitForResponse(time.Second * 3) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while waiting for response from plugin") return nil, serror.New(err) } ap, err := newAvailablePlugin(resp, emitter, ePlugin) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while creating available plugin") return nil, serror.New(err) } if resp.Meta.Unsecure { err = ap.client.Ping() } else { err = ap.client.SetKey() } if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while pinging the plugin") return nil, serror.New(err) } // Get the ConfigPolicy and add it to the loaded plugin c, ok := ap.client.(plugin.Plugin) if !ok { return nil, serror.New(errors.New("missing GetConfigPolicy function")) } cp, err := c.GetConfigPolicy() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "error": err.Error(), "plugin-name": ap.Name(), "plugin-version": ap.Version(), "plugin-id": ap.ID(), }).Error("error in getting config policy") return nil, serror.New(err) } lPlugin.ConfigPolicy = cp if resp.Type == plugin.CollectorPluginType { colClient := ap.client.(client.PluginCollectorClient) cfg := plugin.PluginConfigType{ ConfigDataNode: p.pluginConfig.getPluginConfigDataNode(core.PluginType(resp.Type), resp.Meta.Name, resp.Meta.Version), } metricTypes, err := colClient.GetMetricTypes(cfg) if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-type": "collector", "error": err.Error(), }).Error("error in getting metric types") return nil, serror.New(err) } // Add metric types to metric catalog for _, nmt := range metricTypes { // If the version is 0 default it to the plugin version // This honors the plugins explicit version but falls back // to the plugin version as default if nmt.Version() < 1 { // Since we have to override version we convert to a internal struct nmt = &metricType{ namespace: nmt.Namespace(), version: resp.Meta.Version, lastAdvertisedTime: nmt.LastAdvertisedTime(), config: nmt.Config(), data: nmt.Data(), tags: nmt.Tags(), labels: nmt.Labels(), } } // We quit and throw an error on bad metric versions (<1) // the is a safety catch otherwise the catalog will be corrupted if nmt.Version() < 1 { err := errors.New("Bad metric version from plugin") pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "plugin-name": resp.Meta.Name, "plugin-version": resp.Meta.Version, "plugin-type": resp.Meta.Type.String(), "plugin-path": filepath.Base(lPlugin.Details.ExecPath), "metric-namespace": nmt.Namespace(), "metric-version": nmt.Version(), "error": err.Error(), }).Error("received metric with bad version") return nil, serror.New(err) } p.metricCatalog.AddLoadedMetricType(lPlugin, nmt) } } err = ePlugin.Kill() if err != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": err.Error(), }).Error("load plugin error while killing plugin executable plugin") return nil, serror.New(err) } if resp.State != plugin.PluginSuccess { e := fmt.Errorf("Plugin loading did not succeed: %s\n", resp.ErrorMessage) pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": e, "plugin response": resp.ErrorMessage, }).Error("load plugin error") return nil, serror.New(e) } lPlugin.Meta = resp.Meta lPlugin.Type = resp.Type lPlugin.Token = resp.Token lPlugin.LoadedTime = time.Now() lPlugin.State = LoadedState aErr := p.loadedPlugins.add(lPlugin) if aErr != nil { pmLogger.WithFields(log.Fields{ "_block": "load-plugin", "error": aErr, }).Error("load plugin error while adding loaded plugin to load plugins collection") return nil, aErr } return lPlugin, nil }
// Empty handler acting as placeholder until implementation. This helps tests // pass to ensure registration works. func (r *runner) HandleGomitEvent(e gomit.Event) { switch v := e.Body.(type) { case *control_event.DeadAvailablePluginEvent: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), "aplugin": v.String, }).Warning("handling dead available plugin event") pool, err := r.availablePlugins.getPool(v.Key) if err != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", "aplugin": v.String, }).Error(err.Error()) return } if pool != nil { pool.Kill(v.Id, "plugin dead") } case *control_event.PluginUnsubscriptionEvent: runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.PluginName, "plugin-version": v.PluginVersion, "plugin-type": core.PluginType(v.PluginType).String(), }).Debug("handling plugin unsubscription event") err := r.handleUnsubscription(core.PluginType(v.PluginType).String(), v.PluginName, v.PluginVersion, v.TaskId) if err != nil { return } case *control_event.LoadPluginEvent: var pool strategy.Pool r.availablePlugins.RLock() for key, p := range r.availablePlugins.pools() { // tuple of type name and version // type @ index 0, name @ index 1, version @ index 2 tnv := strings.Split(key, ":") // make sure we don't panic and crash the service if a junk key is retrieved if len(tnv) != 3 { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), "plugin-signed": v.Signed, }).Info("pool has bad key ", key) continue } // attempt to find a pool whose type and name are the same, and whose version is // less than newly loaded plugin. If we find it, break out of loop. if core.PluginType(v.Type).String() == tnv[0] && v.Name == tnv[1] && v.Version > p.Version() { pool = p break } } r.availablePlugins.RUnlock() // now check to see if anything was put where pool points. // if not, there are no older pools whose subscriptions need to be // moved. if pool == nil { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Info("No previous pool found for loaded plugin") return } plugin, err := r.pluginManager.get(fmt.Sprintf("%s:%s:%d", core.PluginType(v.Type).String(), v.Name, v.Version)) if err != nil { return } newPool, err := r.availablePlugins.getOrCreatePool(plugin.Key()) if err != nil { return } subs := pool.MoveSubscriptions(newPool) if len(subs) != 0 { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Info("pool with subscriptions to move found") for _, sub := range subs { r.emitter.Emit(&control_event.PluginSubscriptionEvent{ PluginName: v.Name, PluginVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, SubscriptionType: int(strategy.UnboundSubscriptionType), }) r.emitter.Emit(&control_event.PluginUnsubscriptionEvent{ PluginName: v.Name, PluginVersion: pool.Version(), TaskId: sub.TaskID, PluginType: v.Type, }) r.emitter.Emit(&control_event.MovePluginSubscriptionEvent{ PluginName: v.Name, PreviousVersion: pool.Version(), NewVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, }) } } default: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), }).Info("Nothing to do for this event") } }
// Empty handler acting as placeholder until implementation. This helps tests // pass to ensure registration works. func (r *runner) HandleGomitEvent(e gomit.Event) { switch v := e.Body.(type) { case *control_event.DeadAvailablePluginEvent: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), "aplugin": v.String, }).Warning("handling dead available plugin event") pool, err := r.availablePlugins.getPool(v.Key) if err != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", "aplugin": v.String, }).Error(err.Error()) return } if pool != nil { pool.Kill(v.Id, "plugin dead") } if pool.Eligible() { if pool.RestartCount() < MaxPluginRestartCount { e := r.restartPlugin(v.Key) if e != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", "aplugin": v.String, }).Error(e.Error()) return } pool.IncRestartCount() runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Name, "aplugin": v.Version, "restart_count": pool.RestartCount(), }).Warning("plugin restarted") r.emitter.Emit(&control_event.RestartedAvailablePluginEvent{ Id: v.Id, Name: v.Name, Version: v.Version, Key: v.Key, Type: v.Type, }) } else { r.emitter.Emit(&control_event.MaxPluginRestartsExceededEvent{ Id: v.Id, Name: v.Name, Version: v.Version, Key: v.Key, Type: v.Type, }) } } case *control_event.PluginUnsubscriptionEvent: runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.PluginName, "plugin-version": v.PluginVersion, "plugin-type": core.PluginType(v.PluginType).String(), }).Debug("handling plugin unsubscription event") err := r.handleUnsubscription(core.PluginType(v.PluginType).String(), v.PluginName, v.PluginVersion, v.TaskId) if err != nil { return } case *control_event.UnloadPluginEvent: // On plugin unload, find the key and pool info for the plugin being unloaded. r.availablePlugins.RLock() var pool strategy.Pool var k string for key, p := range r.availablePlugins.table { tnv := strings.Split(key, ":") if core.PluginType(v.Type).String() == tnv[0] && v.Name == tnv[1] && v.Version == p.Version() { pool = p k = key break } } r.availablePlugins.RUnlock() if pool == nil { return } // Check for the highest lower version plugin and move subscriptions that // are not bound to a plugin version to this pool. plugin, err := r.pluginManager.get(fmt.Sprintf("%s:%s:%d", core.PluginType(v.Type).String(), v.Name, -1)) if err != nil { return } newPool, err := r.availablePlugins.getOrCreatePool(plugin.Key()) if err != nil { return } subs := pool.MoveSubscriptions(newPool) // Start new plugins in newPool if needed if newPool.Eligible() { e := r.restartPlugin(plugin.Key()) if e != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", }).Error(e.Error()) return } } // Remove the unloaded plugin from available plugins r.availablePlugins.Lock() delete(r.availablePlugins.table, k) r.availablePlugins.Unlock() pool.RLock() defer pool.RUnlock() if len(subs) != 0 { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Info("pool with subscriptions to move found") for _, sub := range subs { r.emitter.Emit(&control_event.PluginSubscriptionEvent{ PluginName: v.Name, PluginVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, SubscriptionType: int(strategy.UnboundSubscriptionType), }) r.emitter.Emit(&control_event.PluginUnsubscriptionEvent{ PluginName: v.Name, PluginVersion: pool.Version(), TaskId: sub.TaskID, PluginType: v.Type, }) r.emitter.Emit(&control_event.MovePluginSubscriptionEvent{ PluginName: v.Name, PreviousVersion: pool.Version(), NewVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, }) } } case *control_event.LoadPluginEvent: // On loaded plugin event all subscriptions that are not bound to a specific version // need to moved to the loaded version if it's version is greater than the currently // available plugin. var pool strategy.Pool //k := fmt.Sprintf("%v:%v:%v", core.PluginType(v.Type).String(), v.Name, -1) //pool, _ = r.availablePlugins.getPool(k) r.availablePlugins.RLock() currentHighestVersion := -1 for key, p := range r.availablePlugins.pools() { // tuple of type name and version // type @ index 0, name @ index 1, version @ index 2 tnv := strings.Split(key, ":") // make sure we don't panic and crash the service if a junk key is retrieved if len(tnv) != 3 { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), "plugin-signed": v.Signed, }).Info("pool has bad key ", key) continue } // attempt to find a pool whose type and name are the same, and whose version is // less than newly loaded plugin. if core.PluginType(v.Type).String() == tnv[0] && v.Name == tnv[1] && v.Version > p.Version() { // See if the pool version is higher than the current highest. // We only want to move subscriptions from the currentHighest // because that is where subscriptions that are bound to the // latest version will be. if p.Version() > currentHighestVersion { pool = p currentHighestVersion = p.Version() } } } r.availablePlugins.RUnlock() // now check to see if anything was put where pool points. // if not, there are no older pools whose subscriptions need to be // moved. if pool == nil { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Info("No previous pool found for loaded plugin") return } plugin, err := r.pluginManager.get(fmt.Sprintf("%s:%s:%d", core.PluginType(v.Type).String(), v.Name, v.Version)) if err != nil { return } newPool, err := r.availablePlugins.getOrCreatePool(plugin.Key()) if err != nil { return } // Move subscriptions to the new, higher versioned pool subs := pool.MoveSubscriptions(newPool) if newPool.Eligible() { e := r.restartPlugin(plugin.Key()) if e != nil { runnerLog.WithFields(log.Fields{ "_block": "handle-events", }).Error(e.Error()) return } runnerLog.WithFields(log.Fields{ "_block": "pool eligible", }).Info("starting plugin") } pool.RLock() defer pool.RUnlock() if len(subs) != 0 { runnerLog.WithFields(log.Fields{ "_block": "subscribe-pool", "event": v.Namespace(), "plugin-name": v.Name, "plugin-version": v.Version, "plugin-type": core.PluginType(v.Type).String(), }).Info("pool with subscriptions to move found") for _, sub := range subs { r.emitter.Emit(&control_event.PluginSubscriptionEvent{ PluginName: v.Name, PluginVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, SubscriptionType: int(strategy.UnboundSubscriptionType), }) r.emitter.Emit(&control_event.PluginUnsubscriptionEvent{ PluginName: v.Name, PluginVersion: pool.Version(), TaskId: sub.TaskID, PluginType: v.Type, }) r.emitter.Emit(&control_event.MovePluginSubscriptionEvent{ PluginName: v.Name, PreviousVersion: pool.Version(), NewVersion: v.Version, TaskId: sub.TaskID, PluginType: v.Type, }) } } default: runnerLog.WithFields(log.Fields{ "_block": "handle-events", "event": v.Namespace(), }).Info("Nothing to do for this event") } }