About This

The following is a recipe for authoring a plugin that fits smoothly within the snap framework. Like any recipe, the ingredients and the order in which you mix them are important. The major steps are:

  1. Outline your plugin metrics
  2. Decide the CODEC for the plugin
  3. Download or clone Snap
  4. Download or clone snap-plugin-utilities
  5. Implement the required interfaces
  6. Test the plugin
  7. Expose the plugin

Like any good recipe, it will do you well to read the entire document, as well as the Plugin Best Practices, before you start cooking.

Bon Appétit! :stew:

Plugin Authoring

Snap itself runs as a master daemon with the core functionality that may load and unload plugin processes via either CLI or HTTP APIs.

A Snap plugin is a program, or a set of functions or services, written in Go or any language; that may seamlessly integrate with snap as executables.

Communication between Snap and plugins uses RPC either through HTTP or TCP protocols. HTTP JSON-RPC is good for any language to use due to its nature of JSON representation of data while the native client is only suitable for plugins written in Golang. The data that plugins report to snap is in the form of JSON or GOB CODEC.

Before starting writing Snap plugins, check out the Plugin Catalog to see if any suit your needs. If not, you need to reference the plugin packages that defines the type of structures and interfaces inside snap and then write plugin endpoints to implement the defined interfaces.

Plugin Naming, Files, and Directory

Snap supports three type of plugins. They are collectors, processors, and publishers. The plugin project name should use the following format:

snap-plugin-[type]-[name]

For example:

snap-plugin-collector-hana
snap-plugin-processor-movingaverage
snap-plugin-publisher-influxdb

Example files and directory structure:

snap-plugin-[type]-[name]
 |--[name]
  |--[name].go  
  |--[name]_test.go  
  |--[name]_integration_test.go
 |--main.go
 |--main_test.go

Metric Naming

A plugin should NOT advertise metrics which namespaces contain:

a) the following characters in a namespace:
- spaces    ` `
- brackets: `()[]{}`
- slashes:  `| \ /`
- carets:   `^`
- quotations:   `" ' \``
- other punctuations: `. , ; ? !`
b) a wildcard in the end

Example:

Unacceptable metric namespace Why Proposal
/intel/foo/* a wildcard in the end /intel/foo/*/bar
/intel/foo/*/baz
/intel/mock/bar(no) not allowed characters /intel/mock/bar_no
/intel/mock/bar("no") not allowed characters /intel/mock/bar_no
/intel/mock/bar^no not allowed characters /intel/mock/bar_no
/intel/mock/bar.no not allowed characters /intel/mock/bar_no
/intel/mock/bar!? not allowed characters /intel/mock/bar

Snap validates the metrics exposed by plugin and, if validation failed, return an error and not load the plugin.

c) static and dynamic metrics

Snap supports both static and dynamic metrics. You can find more detail about static and dynamic metrics here.

Mandatory packages

There are three mandatory packages that every plugin must use. Other than those three packages, you can use other packages as necessary. There is no danger of colliding dependencies as plugins are separated processes. The mandatory packages are:

github.com/intelsdi-x/snap/control/plugin  
github.com/intelsdi-x/snap/control/plugin/cpolicy  
github.com/intelsdi-x/snap/core/ctypes  

Writing a collector plugin

A Snap collector plugin collects telemetry data by communicating with the Snap daemon. To confine to collector plugin interfaces and metric types defined in Snap, a collector plugin must implement the following methods:

GetConfigPolicy() (*cpolicy.ConfigPolicy, error)
CollectMetrics([]MetricType) ([]MetricType, error)
GetMetricTypes(ConfigType) ([]MetricType, error)

The plugin uses the default values given in the ConfigPolicy so a config file doesn't need to be passed in for these rules. An example use case would be for the URL the Apache Collector collects from. Disclaimer: Two namespaces can't have rules with the same key name. E.g. you can't have the key "username" for /intel/foo/bar and a different "username" for /intel/foo/mock. They would need unique keys.

Writing a processor plugin

A Snap processor plugin allows filtering, aggregation, transformation, etc of collected telemetry data. To complaint with processor plugin interfaces defined in Snap, a processor plugin must implement the following methods:

GetConfigPolicy() (*cpolicy.ConfigPolicy, error)
Process(contentType string, content []byte, config map[string]ctypes.ConfigValue) (string, []byte, error)

Writing a publisher plugin

A Snap publisher plugin allows publishing processed telemetry data into a variety of systems, databases, and monitors through Snap metrics. To compliant with metric types and plugin interfaces defined in Snap, a publisher plugin must implement the following methods:

GetConfigPolicy() (*cpolicy.ConfigPolicy, error)
Publish(contentType string, content []byte, config map[string]ctypes.ConfigValue) error

Exposing a plugin

Creating the main program to serve the newly written plugin as an external process in main.go. By defining "Plugin.PluginMeta" with plugin specific settings, the newly created plugin may have its setting to override Snap global settings. Please refer to a sample to see how main.go is written. You may browse snap global settings.

Building main.go generates a binary executable. You may choose to sign the executable with our plugin signing.

Localization

All comments and READMEs within the plugin code should be in English. For different languages, include appropriate translation files within the plugin package for internationalization.

README

All plugins should have a README with some standard fields:

 1. Snap version requires at least
 2. Snap version tested up to
 3. Supported platforms
 4. Contributor
 5. License

Encryption

Snap provides the encryption capability for both HTTP and TCP clients. The communication between the Snap daemon and the plugins is encrypted by default. Should you want to disable the encrypted communication, when authoring a plugin, use the Unsecure option for your plugin's meta:

//Meta returns the metadata for MyPlugin
func Meta() *plugin.PluginMeta {
    return plugin.NewPluginMeta(name, ver, type, ct, ct2, plugin.Unsecure(true))
}

Logging and debugging

Snap uses logrus to log. Your plugins can use it, or any standard Go log package. Each plugin has its log file. If no logging directory is specified, logs are in the /tmp directory of the running machine. INFO is the logging level for the release version of plugins. Loggers are excellent resources for debugging. You can also use Go GDB or delve to debug.

Building and running the tests

While developing a plugin, unit and integration tests need to be performed. Snap uses goconvey for unit tests. You are welcome to use it or any other unit test framework. For the integration tests, you have to set up $SNAP_PATH and some necessary direct, or indirect dependencies. Using Docker container for integration tests is an effective testing strategy. Integration tests may define an input workflow. Refer to a sample integration test input.

For example, to run a plugin integration test

go test -v tag=integration ./…

For more build and test tips, please refer to our contributing doc.

Distributing plugins

If you think others would find your plugin useful, we encourage you to submit it to our Plugin Catalog for possible inclusion.

License

The Snap framework is released under the Apache 2.0 license.

For more help

Please browse more at our repo or contact the maintainers.