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")
			})
		})
	})
}
Exemple #2
0
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
}