-
Notifications
You must be signed in to change notification settings - Fork 7
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.
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-serverbinary- The example plugins work in this way: when
make pluginsis 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.
- The example plugins work in this way: when
- Plugin files must have the
.soextension - 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.somay make sense (replacing00with 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.
- When a plugin pattern matches multiple files (e.g.,
- 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.
The example Plugins may be built all at once (make plugins) or individually
(e.g., make bin/plugins/s3-images.so).
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.
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.
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.
This section contains the complete list of exposed functions a plugin may expose, and a detailed description of their function signature and behavior.
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.
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.
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.
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.
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).
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.