Developer Guide: Creating new Rundeck Plugin Types
Rundeck's Plugin system can be extended with new Plugin Types, this guide describes how to do so.
Note: WIP document
The Rundeck Plugin type system is organized around "Services" and "Providers".
Plugin Service : Provides some functionality in Rundeck, and has a unique "Service Name", defines a unique Java Interface for plugins
Plugin Provider : Implements functionality of the plugin, has a unique "Provider Name"
Each Plugin Service usually has one Java plugin type. Typically the Java interface for the plugin is named the same as the Service, with "Plugin" as a suffix.
Example:
-
NodeEnhancer
- Service Name -
NodeEnhancerPlugin
- Java Interface name
Services that are internal to Rundeck Core are declared in the ServiceNameConstants
Java class, and associated to the Java interfaces in the ServiceTypes
Java class.
Extract of ServiceNameConstants.java:
...
public static final String NodeEnhancer = "NodeEnhancer";
...
Extract of ServiceTypes.java:
...
map.put(ServiceNameConstants.NodeEnhancer, NodeEnhancerPlugin.class);
...
Additional plugin services can be defined using the Java Service Loader pattern.
Implement the PluginProviderServices
interface.
In addition to defining the service name to type mapping, this can also be used to define the "Service Provider Loader" for types, if necessary. (See below).
Note: For most cases, you don't need to implement these loaders, they can be created for you automatically. In the case of basic Java types, you don't need to implement the provider loader.
The internal mechanism for loading plugin implementations is via "Provider Loaders". These classes are responsible for instantiating Java objects which implement the appropriate interface for a specific Plugin Service.
If you want "Script Plugin" functionality for your plugin type, you will need to define a Provider Loader for this behavior. (See Script Plugin Loaders below).
Each Provider definition can define metadata about the provider, known as the "Description":
- Provider name
- Display information (title, description)
- Input properties
- Other arbitrary metadata (e.g. predfined icon name)
Plugin Files are the means of distributing Rundeck plugins. They come in three formats:
- Java Jar file - a normal Jar file containing Java code. Metadata about the plugin providers and code is provided in the Jar Manifest
- Zip file - a zip file containing script code. Metadata about the plugin providers is provided in a file called
plugin.yaml
- Groovy file - a
.groovy
source file. Some metadata about the plugin can be provided within the Groovy Rundeck Plugin DSL.
Plugin "loading" refers to two things:
- A) A plugin file is "loaded" into Rundeck after it is installed, and it is scanned to determine the service providers it defines.
- B) A plugin "instance" (java object) is "loaded" via the PluginService, to create a new object which implements the service interface.
Rundeck internally manages part A, while Service Provider Loaders manage part B.
Rundeck scans the plugins directory (libext
) for .jar
files, and uses the Manifest information to get a list of Java class names that are plugin Provider types within the Jar file. Rundeck uses Java class loading and reflection to scan the classes to determine the Service Provider types available, and the Provider Metadata.
To load an instance of one of the providers, the Java class is instantiated.
This is the most most basic way to load plugins, and will be done automatically in most cases, without needing to define a "Service Provider Loader". However, in some cases (e.g. if all instances of your plugin type need a particular constructor argument), you can implement this yourself.
Rundeck scans the plugins directory for .zip
files. It extracts the plugin.yaml
file, and parses it to determine the Provider definitions within the contents of the zip. The plugin.yaml
specifies which script files in the zip are used for the provider implementations, and how to pass arguments to them, as well as Provider Metadata information.
To load an instance of one of these providers, Rundeck provides an object that implements the correct Service interface, and uses the plugin.yaml
Provider definition and metadata to run the script file from within the zip.
To create a new type of script plugin, you must implement a Service Provider Loader that can use the Script Plugin Provider Definition and Metadata to execute the specified script, or provide the necessary functionality.
At startup* Rundeck scans the plugins directory for .groovy
script files. It loads these Groovy Scripts, and injects a rundeckPlugin
method, which provides a DSL for certain plugin types. The DSL provides a Groovy Builder mechanism to define the Provider detail, metadata, and custom methods used by the service interface.
To load an instance of one of these providers, Rundeck looks for Spring Beans which implement a specific Builder interface, and calls that to build the instance which implements the Service Type.
To create a new type of Groovy plugin DSL, you must define an appropriate builder class to provide the custom DSL methods, and "build" an instance conforming to the service type interface.
In addition to plugin files (externally loaded), Rundeck can define internal provider implementations which are defined a Spring Beans in the Rundeck application. Beans which are intended to be loaded internally as Plugin Provider Implementations must register themselves with the Plugin Registry. Typically these beans implement the Spring FactoryBean
interface, to create new instances when requested.
- Define the Java interface for the plugin
- Define the Service Name for the Plugin Service
- Add it to ServiceNameConstants
- (Optional) Define a Service Provider Loader, if you need custom Java instance loading behavior, or you want to implement a Script Plugin mechanism
- (Optional) Define a Groovy PluginBuilder which can implement the Service Interface and use Groovy Builder pattern to provide closures to call to implement behavior.
- Determine the lifecycle of your plugin type:
- Is the plugin loaded globally? per-project? at some other level?
- When is it disposed of?
- Use the
pluginService
to list available providers Descriptions - Use the
pluginService
to validate user input for any Plugin Properties - Use the
pluginService
to "configure" (instantiate with plugin property input values) an instance of the plugin- (Optional) Use the "retainPlugin" method, which returns a "closeable" form of the configured plugin instance. Use this method to ensure the plugin file loader does not unload the plugin definition until all closeable instances are closed
JavaClassProviderLoadable
ScriptPluginProviderLoadable
Create a new directory under the /examples
directory in the rundeck repo, containing a complete example project to build a zip or jar of your plugin type. It should be named example-java-myplugin
for example. See existing examples for examples. example.