func TestAvailablePlugin(t *testing.T) { Convey("newAvailablePlugin()", t, func() { Convey("returns an availablePlugin", func() { ln, _ := net.Listen("tcp", ":4000") defer ln.Close() resp := &plugin.Response{ Meta: plugin.PluginMeta{ Name: "testPlugin", Version: 1, }, Type: plugin.CollectorPluginType, ListenAddress: "127.0.0.1:4000", } ap, err := newAvailablePlugin(resp, nil, nil) So(ap, ShouldHaveSameTypeAs, new(availablePlugin)) So(err, ShouldBeNil) }) }) Convey("Stop()", t, func() { Convey("returns nil if plugin successfully stopped", func() { r := newRunner(&routing.RoundRobinStrategy{}) a := plugin.Arg{ PluginLogPath: "/tmp/snap-test-plugin-stop.log", } exPlugin, _ := plugin.NewExecutablePlugin(a, PluginPath) ap, err := r.startPlugin(exPlugin) So(err, ShouldBeNil) err = ap.Stop("testing") So(err, ShouldBeNil) }) }) }
func TestAvailablePlugin(t *testing.T) { Convey("newAvailablePlugin()", t, func() { Convey("returns an availablePlugin", func() { ln, _ := net.Listen("tcp", ":4000") defer ln.Close() resp := plugin.Response{ Meta: plugin.PluginMeta{ Name: "testPlugin", Version: 1, }, Type: plugin.CollectorPluginType, ListenAddress: "127.0.0.1:4000", } ap, err := newAvailablePlugin(resp, nil, nil) So(ap, ShouldHaveSameTypeAs, new(availablePlugin)) So(err, ShouldBeNil) }) }) Convey("Stop()", t, func() { Convey("returns nil if plugin successfully stopped", func() { r := newRunner() r.SetEmitter(new(MockEmitter)) a := plugin.Arg{} exPlugin, _ := plugin.NewExecutablePlugin(a, fixtures.PluginPathMock2) ap, err := r.startPlugin(exPlugin) So(err, ShouldBeNil) err = ap.Stop("testing") So(err, ShouldBeNil) }) }) }
func newExecutablePlugin(a plugin.Arg, path string) (*plugin.ExecutablePlugin, error) { // Travis optimization: Try starting the plugin three times before finally // returning an error var e error var ep *plugin.ExecutablePlugin for i := 0; i < 3; i++ { ep, e = plugin.NewExecutablePlugin(a, path) if e == nil { break } if e != nil && i == 2 { return nil, e } } return ep, nil }
func (r *runner) runPlugin(details *pluginDetails) error { if details.IsPackage { f, err := os.Open(details.Path) if err != nil { return err } defer f.Close() tempPath, err := aci.Extract(f) if err != nil { return err } details.ExecPath = path.Join(tempPath, "rootfs") } ePlugin, err := plugin.NewExecutablePlugin(r.pluginManager.GenerateArgs(int(log.GetLevel())), path.Join(details.ExecPath, details.Exec)) if err != nil { runnerLog.WithFields(log.Fields{ "_block": "run-plugin", "path": path.Join(details.ExecPath, details.Exec), "error": err, }).Error("error creating executable plugin") return err } ap, err := r.startPlugin(ePlugin) if err != nil { runnerLog.WithFields(log.Fields{ "_block": "run-plugin", "path": path.Join(details.ExecPath, details.Exec), "error": err, }).Error("error starting new plugin") return err } ap.exec = details.Exec ap.execPath = details.ExecPath if details.IsPackage { ap.fromPackage = true } return 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(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 }