Skip to content

Plugins

Jeremy Echols edited this page Feb 24, 2020 · 18 revisions

RAIS Plugins

RAIS has a rudimentary plugin system in place based on the way Go plugins work. Developers looking to create a plugin should read the Go plugin documentation.

Example plugins can be built via make plugins or, if using docker, ./scripts/buildrun.sh make plugins. Plugins are configured via a "Plugins" entry in /etc/rais.toml, an environment variable named RAIS_PLUGINS, or a command-line flag, --plugins. However they're configured, plugins must be explicitly loaded (unlike in v3.0.x where they auto-loaded).

Note: the external images plugin is not secure. It is meant for demonstration only, and should not be used in a live server. It's safe to build so long as you just don't configure plugins to all load blindly (or else remove it prior to running RAIS: rm plugins/external-images.so)

Note II: To revert to the v3.0.x plugin-loading behavior, simply set Plugins to "*.so" (or export RAIS_PLUGINS="*.so"). This will load all plugins in the plugins directory in order of their filenames.

Behavior

General behavior of plugins:

  • Plugin patterns that don't start with a slash are loaded from within a "plugins" directory which lives alongside the rais-server binary
    • The example plugins work in this way: when make plugins is run, the plugins are generated in ./bin/plugins/*.so, whereas the server binary is ./bin/rais-server. If your plugins pattern is *.so, you'll load all plugins when RAIS starts.
  • Plugin files must have the .so extension
  • Plugin order matters!
    • When a plugin pattern matches multiple files (e.g., Plugins="*.so"), plugins will be processed in alphabetic order.
    • If you need plugins to have clear priorities, but you still want to load them all via a wildcard pattern, a naming scheme such as 00-name.so may make sense (replacing 00 with a two-digit load order value).
    • If you specify plugins individually (e.g., Plugins=s3-images.so,external-images.so), they're loaded in the order you specify.
  • If you specify the same plugin multiple times, even on accident (e.g., Plugins=s3-images.so,*.so, RAIS won't start
  • Plugins must be compiled on the same architecture that is used to compile the RAIS server. This means the same server, the same openjpeg libraries, and the same version of Go.

Example plugins

The example Plugins may be built all at once (make plugins) or individually (e.g., make bin/plugins/s3-images.so).

External Images

The example external-images plugin showcases how a plugin might be built to alter how RAIS interprets certain IDs. The example allows external images to be downloaded and converted to JP2s on the fly. Though it would be an unusual use-case, this sort of plugin (with some tweaks) could provide IIIF image features for external images you don't control, as happens with third-party digital asset management systems.

To see how to use this plugin, see our External Images Plugin documentation. Note that this plugin is not production-ready.

S3 Images

The s3-images plugin is a production-ready plugin that pulls images from S3 on demand. As it's a production-ready plugin, comprehensive documentation is found on the S3 Images Plugin page.

DataDog

The datadog plugin shows the use of WrapHandler (see below) by adding the DataDog tracing agent to all clients' requests. This allows high-level performance monitoring with minimal code.

This is mostly an example (though it is production-ready if the behavior makes sense for you), so the documentation is primarily contained within the main.go file.

Plugin Variables / Functions

This section contains the complete list of exposed functions a plugin may expose, and a detailed description of their function signature and behavior.

SetLogger

func SetLogger(raisLogger *logger.Logger)

All plugins may define this function, and there are no side-effects to worry about when defining it (regarding ordering or multiple plugins "competing"). This function allows a plugin to make use of the central RAIS log subsystem so that logged messages can be consistent. Plugins don't have to expose this function if they aren't logging any messages.

logger.Logger is defined by package github.com/uoregon-libraries/gopkg/logger.

Initialize

func Initialize()

All plugins may define this function. This can be used to handle things like custom configuration a plugin may need. See the s3 plugin's Initialize method for an example of that.

Initialize() is run after the logger is set up (unlike Go's internal init() function), so you can safely use it.

Enabled

Added in v3.1.0

var Enabled bool

Enabled lets a plugin state to the plugin manager that it is explicitly enabled. If it's set to false, that typically means something happened in its Initialize() function that prevented it from being usable. The S3 plugin uses this to prevent its exposed functions from being used when bad configuration is detected, for instance.

WrapHandler

func WrapHandler(pattern string, handler http.Handler) (http.Handler, error)

WrapHandler is called three times in RAIS: once for the main IIIF handler, once for the experimental DZI handler, and finally for the /version handler. It is meant only as a very high-level wrapper for the moment, and doesn't (easily) allow adding custom handlers to RAIS.

A plugin implementing WrapHandler can use the pattern to identify the path being wrapped -- though the IIIF path is variable, so this can be used for logging or other "identity" logic, but not easily for filtering. The handler passed in is the current state of the handler. A wrapper could add middleware, logging, etc. See the datadog plugin for an example.

Any number of plugins can implement WrapHandler. Each plugin's returned handler is sent to the next plugin in the list.

If a plugin handles this function, but needs to skip a particular pattern, it should return nil, plugins.ErrSkipped. This indicates to RAIS that the plugin didn't fail, but simply chose to avoid trying to handle the given pattern and handler.

Teardown

func Teardown()

All plugins may define a Teardown function for handling any necessary cleanup. This is called when RAIS is about to exit, though it is not guaranteed to be called (for instance, if power goes out or the server is force-killed).

IDToPath

func IDToPath(id iiif.ID) (path string, err error)

If the given ID isn't handled by this plugin, plugins.ErrSkipped should be returned by the plugin (the path returned will be ignored).

The first plugin which returns a nil error "wins". If there are multiple plugins trying to convert IDs to paths, you must be sure you put them in a sensible order.

Clone this wiki locally