Skip to content

Developer Guide: Creating new Rundeck Plugin Types

Greg Schueler edited this page Dec 12, 2019 · 1 revision

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

Overview

Plugin Services and Plugin Providers

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

Internal Plugin Services

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);
...

External Plugin Services

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).

Service Provider Loaders

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).

Provider Description

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

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

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.

Loading Jar Plugin Files

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.

Loading Script Plugin Files

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.

Loading Groovy Plugin Files

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.

Loading Internal Plugins

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.

Defining a plugin type

  1. Define the Java interface for the plugin
  2. Define the Service Name for the Plugin Service
    • Add it to ServiceNameConstants
  3. (Optional) Define a Service Provider Loader, if you need custom Java instance loading behavior, or you want to implement a Script Plugin mechanism
  4. (Optional) Define a Groovy PluginBuilder which can implement the Service Interface and use Groovy Builder pattern to provide closures to call to implement behavior.
  5. Determine the lifecycle of your plugin type:
    • Is the plugin loaded globally? per-project? at some other level?
    • When is it disposed of?
  6. Use the pluginService to list available providers Descriptions
  7. Use the pluginService to validate user input for any Plugin Properties
  8. 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

Defining a Service Provider Loader

JavaClassProviderLoadable ScriptPluginProviderLoadable

Creating Example Code

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.