func TestPluginRestCalls(t *testing.T) { CompressedUpload = false Convey("REST API functional V1", t, func() { Convey("Load Plugin - POST - /v1/plugins", func() { Convey("a single plugin loads", func() { // This test alone tests gzip. Saves on test time. CompressedUpload = true r := startAPI() port := r.port col := core.CollectorPluginType pub := core.PublisherPluginType Convey("A global plugin config is added for all plugins", func() { cdn := cdata.NewNode() cdn.AddItem("password", ctypes.ConfigValueStr{Value: "p@ssw0rd"}) r := setPluginConfigItem(port, "", "", "", cdn) So(r.Body, ShouldHaveSameTypeAs, &rbody.SetPluginConfigItem{}) r1 := r.Body.(*rbody.SetPluginConfigItem) So(r1.Table()["password"], ShouldResemble, ctypes.ConfigValueStr{Value: "p@ssw0rd"}) r2 := getPluginConfigItem(port, &col, "", "") So(r2.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r3 := r2.Body.(*rbody.PluginConfigItem) So(len(r3.Table()), ShouldEqual, 1) So(r3.Table()["password"], ShouldResemble, ctypes.ConfigValueStr{Value: "p@ssw0rd"}) Convey("A plugin config is added for all publishers", func() { cdn := cdata.NewNode() cdn.AddItem("user", ctypes.ConfigValueStr{Value: "john"}) r := setPluginConfigItem(port, core.PublisherPluginType.String(), "", "", cdn) So(r.Body, ShouldHaveSameTypeAs, &rbody.SetPluginConfigItem{}) r1 := r.Body.(*rbody.SetPluginConfigItem) So(r1.Table()["user"], ShouldResemble, ctypes.ConfigValueStr{Value: "john"}) So(len(r1.Table()), ShouldEqual, 2) Convey("A plugin config is added for all versions of a publisher", func() { cdn := cdata.NewNode() cdn.AddItem("path", ctypes.ConfigValueStr{Value: "/usr/local/influxdb/bin"}) r := setPluginConfigItem(port, "2", "influxdb", "", cdn) So(r.Body, ShouldHaveSameTypeAs, &rbody.SetPluginConfigItem{}) r1 := r.Body.(*rbody.SetPluginConfigItem) So(r1.Table()["path"], ShouldResemble, ctypes.ConfigValueStr{Value: "/usr/local/influxdb/bin"}) So(len(r1.Table()), ShouldEqual, 3) Convey("A plugin config is added for a specific version of a publisher", func() { cdn := cdata.NewNode() cdn.AddItem("rate", ctypes.ConfigValueFloat{Value: .8}) r := setPluginConfigItem(port, core.PublisherPluginType.String(), "influxdb", "1", cdn) So(r.Body, ShouldHaveSameTypeAs, &rbody.SetPluginConfigItem{}) r1 := r.Body.(*rbody.SetPluginConfigItem) So(r1.Table()["rate"], ShouldResemble, ctypes.ConfigValueFloat{Value: .8}) So(len(r1.Table()), ShouldEqual, 4) r2 := getPluginConfigItem(port, &pub, "", "") So(r2.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r3 := r2.Body.(*rbody.PluginConfigItem) So(len(r3.Table()), ShouldEqual, 2) r4 := getPluginConfigItem(port, &pub, "influxdb", "1") So(r4.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r5 := r4.Body.(*rbody.PluginConfigItem) So(len(r5.Table()), ShouldEqual, 4) Convey("A global plugin config field is deleted", func() { r := deletePluginConfigItem(port, "", "", "", []string{"password"}) So(r.Body, ShouldHaveSameTypeAs, &rbody.DeletePluginConfigItem{}) r1 := r.Body.(*rbody.DeletePluginConfigItem) So(len(r1.Table()), ShouldEqual, 0) r2 := setPluginConfigItem(port, core.PublisherPluginType.String(), "influxdb", "", cdn) So(r2.Body, ShouldHaveSameTypeAs, &rbody.SetPluginConfigItem{}) r3 := r2.Body.(*rbody.SetPluginConfigItem) So(len(r3.Table()), ShouldEqual, 3) }) }) }) }) }) }) Convey("Plugin config is set at startup", func() { cfg := control.NewConfig() b, err := ioutil.ReadFile("../../examples/configs/snap-config-sample.json") So(err, ShouldBeNil) json.Unmarshal(b, cfg) r := startAPI(control.OptSetConfig(cfg)) port := r.port col := core.CollectorPluginType Convey("Gets the collector config by name and version", func() { r := getPluginConfigItem(port, &col, "pcm", "1") So(r.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r1 := r.Body.(*rbody.PluginConfigItem) So(r1.Table()["path"], ShouldResemble, ctypes.ConfigValueStr{Value: "/usr/local/pcm/bin"}) So(r1.Table()["user"], ShouldResemble, ctypes.ConfigValueStr{Value: "john"}) So(len(r1.Table()), ShouldEqual, 6) }) Convey("Gets the config for a collector by name", func() { r := getPluginConfigItem(port, &col, "pcm", "") So(r.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r1 := r.Body.(*rbody.PluginConfigItem) So(r1.Table()["path"], ShouldResemble, ctypes.ConfigValueStr{Value: "/usr/local/pcm/bin"}) So(r1.Table()["user"], ShouldResemble, ctypes.ConfigValueStr{Value: "jane"}) So(len(r1.Table()), ShouldEqual, 3) }) Convey("Gets the config for all collectors", func() { r := getPluginConfigItem(port, &col, "", "") So(r.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r1 := r.Body.(*rbody.PluginConfigItem) So(r1.Table()["user"], ShouldResemble, ctypes.ConfigValueStr{Value: "jane"}) So(r1.Table()["password"], ShouldResemble, ctypes.ConfigValueStr{Value: "p@ssw0rd"}) So(len(r1.Table()), ShouldEqual, 2) }) Convey("Gets the config for all plugins", func() { r := getPluginConfigItem(port, nil, "", "") So(r.Body, ShouldHaveSameTypeAs, &rbody.PluginConfigItem{}) r1 := r.Body.(*rbody.PluginConfigItem) So(r1.Table()["password"], ShouldResemble, ctypes.ConfigValueStr{Value: "p@ssw0rd"}) So(len(r1.Table()), ShouldEqual, 1) }) }) }) Convey("Enable task - put - /v1/tasks/:id/enable", func() { Convey("Enable a running task", func(c C) { r := startAPI() port := r.port uploadPlugin(MOCK_PLUGIN_PATH2, port) uploadPlugin(FILE_PLUGIN_PATH, port) r1 := createTask("1.json", "yeti", "1s", true, port) So(r1.Body, ShouldHaveSameTypeAs, new(rbody.AddScheduledTask)) plr1 := r1.Body.(*rbody.AddScheduledTask) id := plr1.ID r2 := startTask(id, port) So(r2.Body, ShouldHaveSameTypeAs, new(rbody.ScheduledTaskStarted)) plr2 := r2.Body.(*rbody.ScheduledTaskStarted) So(plr2.ID, ShouldEqual, id) r4 := enableTask(id, port) So(r4.Body, ShouldHaveSameTypeAs, new(rbody.Error)) plr4 := r4.Body.(*rbody.Error) So(plr4.ErrorMessage, ShouldEqual, "Task must be disabled") }) }) }) }
func action(ctx *cli.Context) { // If logPath is set, we verify the logPath and set it so that all logging // goes to the log file instead of stdout. logPath := ctx.String("log-path") if logPath != "" { f, err := os.Stat(logPath) if err != nil { log.Fatal(err) } if !f.IsDir() { log.Fatal("log path provided must be a directory") } file, err := os.OpenFile(fmt.Sprintf("%s/snap.log", logPath), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatal(err) } defer file.Close() log.SetOutput(file) } var l = map[int]string{ 1: "debug", 2: "info", 3: "warning", 4: "error", 5: "fatal", } var t = map[int]string{ 0: "disabled", 1: "enabled", 2: "warning", } logLevel := ctx.Int("log-level") maxProcs := ctx.Int("max-procs") disableAPI := ctx.Bool("disable-api") apiPort := ctx.Int("api-port") autodiscoverPath := ctx.String("auto-discover") maxRunning := ctx.Int("max-running-plugins") pluginTrust := ctx.Int("plugin-trust") keyringPaths := ctx.String("keyring-files") cachestr := ctx.String("cache-expiration") isTribeEnabled := ctx.Bool("tribe") tribeSeed := ctx.String("tribe-seed") tribeNodeName := ctx.String("tribe-node-name") tribeAddr := ctx.String("tribe-addr") tribePort := ctx.Int("tribe-port") cache, err := time.ParseDuration(cachestr) if err != nil { log.Fatal(fmt.Sprintf("invalid cache-expiration format: %s", cachestr)) } config := ctx.String("config") restHttps := ctx.Bool("rest-https") restKey := ctx.String("rest-key") restCert := ctx.String("rest-cert") log.Info("Starting snapd (version: ", gitversion, ")") // Set Max Processors for snapd. setMaxProcs(maxProcs) // Validate log level and trust level settings for snapd validateLevelSettings(logLevel, pluginTrust) controlOpts := []control.PluginControlOpt{ control.MaxRunningPlugins(maxRunning), control.CacheExpiration(cache), } if config != "" { b, err := ioutil.ReadFile(config) if err != nil { log.WithFields(log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "path": config, }).Fatal("unable to read config") } cfg := control.NewConfig() err = json.Unmarshal(b, &cfg) if err != nil { log.WithFields(log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "path": config, }).Fatal("invalid config") } controlOpts = append(controlOpts, control.OptSetConfig(cfg)) } c := control.New( controlOpts..., ) coreModules = []coreModule{} coreModules = append(coreModules, c) s := scheduler.New( scheduler.CollectQSizeOption(defaultQueueSize), scheduler.CollectWkrSizeOption(defaultPoolSize), scheduler.PublishQSizeOption(defaultQueueSize), scheduler.PublishWkrSizeOption(defaultPoolSize), scheduler.ProcessQSizeOption(defaultQueueSize), scheduler.ProcessWkrSizeOption(defaultPoolSize), ) s.SetMetricManager(c) coreModules = append(coreModules, s) var tr managesTribe if isTribeEnabled { log.Info("Tribe is enabled") tc := tribe.DefaultConfig(tribeNodeName, tribeAddr, tribePort, tribeSeed, apiPort) t, err := tribe.New(tc) if err != nil { printErrorAndExit(t.Name(), err) } c.RegisterEventHandler("tribe", t) t.SetPluginCatalog(c) s.RegisterEventHandler("tribe", t) t.SetTaskManager(s) coreModules = append(coreModules, t) tr = t } // Set interrupt handling so we can die gracefully. startInterruptHandling(coreModules...) // Start our modules var started []coreModule for _, m := range coreModules { if err := startModule(m); err != nil { for _, m := range started { m.Stop() } printErrorAndExit(m.Name(), err) } started = append(started, m) } //Plugin Trust c.SetPluginTrustLevel(pluginTrust) log.Info("setting plugin trust level to: ", t[pluginTrust]) //Keyring checking for trust levels 1 and 2 if pluginTrust > 0 { keyrings := filepath.SplitList(keyringPaths) if len(keyrings) == 0 { log.WithFields( log.Fields{ "block": "main", "_module": "snapd", }).Fatal("need keyring file when trust is on (--keyring-file or -k)") } for _, k := range keyrings { keyringPath, err := filepath.Abs(k) if err != nil { log.WithFields( log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "keyringPath": keyringPath, }).Fatal("Unable to determine absolute path to keyring file") } f, err := os.Stat(keyringPath) if err != nil { log.WithFields( log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "keyringPath": keyringPath, }).Fatal("bad keyring file") } if f.IsDir() { log.Info("Adding keyrings from: ", keyringPath) files, err := ioutil.ReadDir(keyringPath) if err != nil { log.WithFields( log.Fields{ "_block": "main", "_module": "snapd", "error": err.Error(), "keyringPath": keyringPath, }).Fatal(err) } if len(files) == 0 { log.Fatal(fmt.Sprintf("given keyring path [%s] is an empty directory!", keyringPath)) } for _, keyringFile := range files { if keyringFile.IsDir() { continue } if strings.HasSuffix(keyringFile.Name(), ".gpg") || (strings.HasSuffix(keyringFile.Name(), ".pub")) || (strings.HasSuffix(keyringFile.Name(), ".pubring")) { f, err := os.Open(keyringPath) if err != nil { log.WithFields( log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "keyringPath": keyringPath, }).Warning("unable to open keyring file. not adding to keyring path") continue } f.Close() log.Info("adding keyring file: ", keyringPath+"/"+keyringFile.Name()) c.SetKeyringFile(keyringPath + "/" + keyringFile.Name()) } } } else { f, err := os.Open(keyringPath) if err != nil { log.WithFields( log.Fields{ "block": "main", "_module": "snapd", "error": err.Error(), "keyringPath": keyringPath, }).Fatal("unable to open keyring file.") } f.Close() log.Info("adding keyring file ", keyringPath) c.SetKeyringFile(keyringPath) } } } //Autodiscover if autodiscoverPath != "" { log.Info("auto discover path is enabled") paths := filepath.SplitList(autodiscoverPath) c.SetAutodiscoverPaths(paths) for _, p := range paths { fullPath, err := filepath.Abs(p) if err != nil { log.WithFields( log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": p, }).Fatal(err) } log.Info("autoloading plugins from: ", fullPath) files, err := ioutil.ReadDir(fullPath) if err != nil { log.WithFields( log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": fullPath, }).Fatal(err) } for _, file := range files { if file.IsDir() { continue } if strings.HasSuffix(file.Name(), ".aci") || !(strings.HasSuffix(file.Name(), ".asc")) { rp, err := core.NewRequestedPlugin(path.Join(fullPath, file.Name())) if err != nil { log.WithFields(log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": fullPath, "plugin": file, }).Error(err) } signatureFile := file.Name() + ".asc" if _, err := os.Stat(path.Join(fullPath, signatureFile)); err == nil { err = rp.ReadSignatureFile(path.Join(fullPath, signatureFile)) if err != nil { log.WithFields(log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": fullPath, "plugin": file.Name() + ".asc", }).Error(err) } } pl, err := c.Load(rp) if err != nil { log.WithFields(log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": fullPath, "plugin": file, }).Error(err) } else { log.WithFields(log.Fields{ "_block": "main", "_module": "snapd", "autodiscoverpath": fullPath, "plugin": file, "plugin-name": pl.Name(), "plugin-version": pl.Version(), "plugin-type": pl.TypeName(), }).Info("Loading plugin") } } } } } else { log.Info("auto discover path is disabled") } //API if !disableAPI { r, err := rest.New(restHttps, restCert, restKey) if err != nil { log.Fatal(err) return } r.BindMetricManager(c) r.BindConfigManager(c.Config) r.BindTaskManager(s) if tr != nil { r.BindTribeManager(tr) } r.Start(fmt.Sprintf(":%d", apiPort)) go monitorErrors(r.Err()) log.Info("Rest API is enabled") } else { log.Info("Rest API is disabled") } log.WithFields( log.Fields{ "block": "main", "_module": "snapd", }).Info("snapd started") // Switch log level to user defined log.Info("setting log level to: ", l[logLevel]) log.SetLevel(getLevel(logLevel)) select {} //run forever and ever }