// UnloadPlugin unloads a plugin from the LoadedPlugins table func (p *pluginManager) UnloadPlugin(pl core.Plugin) (*loadedPlugin, serror.SnapError) { plugin, err := p.loadedPlugins.get(fmt.Sprintf("%s:%s:%d", pl.TypeName(), pl.Name(), pl.Version())) if err != nil { se := serror.New(ErrPluginNotFound, map[string]interface{}{ "plugin-name": pl.Name(), "plugin-version": pl.Version(), "plugin-type": pl.TypeName(), }) return nil, se } if plugin.State != LoadedState { se := serror.New(ErrPluginNotInLoadedState, map[string]interface{}{ "plugin-name": plugin.Name(), "plugin-version": plugin.Version(), "plugin-type": pl.TypeName(), }) return nil, se } // If the plugin has been uploaded via REST API // aka, was not auto loaded from auto_discover_path // nor loaded from tests // then do clean up if !plugin.Details.IsAutoLoaded { pmLogger.WithFields(log.Fields{ "plugin-type": plugin.TypeName(), "plugin-name": plugin.Name(), "plugin-version": plugin.Version(), "plugin-path": plugin.Details.Path, }).Debugf("Removing plugin") if err := os.RemoveAll(filepath.Dir(plugin.Details.Path)); err != nil { pmLogger.WithFields(log.Fields{ "plugin-type": plugin.TypeName(), "plugin-name": plugin.Name(), "plugin-version": plugin.Version(), "plugin-path": plugin.Details.Path, }).Error(err) se := serror.New(err) se.SetFields(map[string]interface{}{ "plugin-type": plugin.TypeName(), "plugin-name": plugin.Name(), "plugin-version": plugin.Version(), "plugin-path": plugin.Details.Path, }) return nil, se } } p.loadedPlugins.remove(plugin.Key()) // Remove any metrics from the catalog if this was a collector if plugin.TypeName() == "collector" { p.metricCatalog.RmUnloadedPluginMetrics(plugin) } return plugin, 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") } }