There are currently two ways to develop plugins:
Either way, the resultant plugin archive file, either a .jar java archive, or a .zip file archive, will be placed in the plugin directory (Launcher: $RDECK_BASE/libext
, RPM,DEB: /var/lib/rundeck/libext
).
Java plugins are distributed as .jar files containing the necessary classes for one or more service provider, as well as any other java jar dependency files.
Each classname listed must be a valid "Provider Class" as defined below, and must have a @Plugin
annotation to define it's service type and provider name.
The .jar
file you distribute must have this metadata within the main Manifest for the jar file to be correctly loaded by the system:
Rundeck-Plugin-Version: 1.1
Rundeck-Plugin-Archive: true
Rundeck-Plugin-Classnames: classname,..
Rundeck-Plugin-Libs: lib/something.jar ...
(optional)Additionally, you should include a manifest entry to indicate the plugin file's version:
Rundeck-Plugin-File-Version: 1.x
This version number will be used to load only the newest plugin file, if more than one provider of the same name and type is defined.
Rundeck's core jar is published to the central Maven repository, so you can simply specify a dependency in your build file.
For gradle, use:
compile(group:'org.rundeck', name: 'rundeck-core', version: '2.6.2')
For maven use:
<dependencies>
<dependency>
<groupId>org.rundeck</groupId>
<artifactId>rundeck-core</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
If your Java classes require external libraries that are not included with the Rundeck runtime, you can include them in your .jar archive. (Look in $RDECK_BASE/tools/lib
to see the set of third-party jars that are available for your classes by default at runtime).
Specify the Rundeck-Plugin-Libs
attribute in the Main attributes of the Manifest for the jar, set the value to a space-separated list of jar file names as you have included them in the jar.
E.g.:
Rundeck-Plugin-Libs: lib/somejar-1.2.jar lib/anotherjar-1.3.jar
Then include the jar files in the Plugin's jar contents:
META-INF/
META-INF/MANIFEST.MF
com/
com/mycompany/
com/mycompany/rundeck/
com/mycompany/rundeck/plugin/
com/mycompany/rundeck/plugin/test/
com/mycompany/rundeck/plugin/test/TestNodeExecutor.class
lib/
lib/somejar-1.2.jar
lib/anotherjar-1.3.jar
The Rundeck core makes use of several different "Services" that provide functionality for executing steps, getting information about Nodes or sending notifications.
Plugins can contain one or more Service Provider implementations. Each plugin file could contain multiple Providers for different types of services, however typically each plugin file would contain only providers related in some fashion.
Node Execution services:
Resource model services:
ResourceModelSource
- produces a set of Node definitions for a project javadoc.ResourceFormatParser
- parses a document into a set of Node resources javadoc.ResourceFormatGenerator
- generates a document from a set of Node resources javadoc.Workflow Step services (described in Workflow Step Plugin):
WorkflowStep
- runs a single step in a workflow.WorkflowNodeStep
- runs a single step for each node in a workflow.RemoteScriptNodeStep
- generates a script or command to execute remotely for each node in a workflow.Notification services (described in Notification Plugin):
Notification
- performs an action after a Job state trigger.Storage services:
Storage
- backend for storing data: Storage PluginStorageConverter
- modifies stored content or metadata: Storage Converter PluginLogging services:
ExecutionFileStorage
- stores and retrieves execution files to another location: Execution File Storage PluginStreamingLogWriter
- writes execution log events to a destination: Streaming Log Writer PluginStreamingLogReader
- reads execution log events from a destination: Streaming Log Reader PluginOrchestrator:
Orchestrator
- orchestrates node dispatching: Orchestrator PluginA "Provider Class" is a java class that implements a particular interface and declares itself as a provider for a particular Rundeck "Service".
Each plugin also defines a "Name" that identifies it for use in Rundeck. The Name of a plugin is also referred to as a "Provider Name", as the plugin class is a provider of a particular service.
You should choose a unique but simple name for your provider.
Each plugin class must have the Plugin annotation applied to it.
@Plugin(name="myprovider", service="NodeExecutor")
public class MyProvider implements NodeExecutor {
...
}
Your provider class must have at least a zero-argument constructor, and optionally can have a single-argument constructor with a com.dtolabs.rundeck.core.common.Framework
parameter, in which case your class will be constructed with this constructor and passed the Framework instance.
You may log messages to the ExecutionListener available via ExecutionContext#getExecutionListener() method.
You can also send output to System.err
and System.out
and it will be captured as output of the execution.
Provider classes are instantiated when needed by the Framework object, and the instance is retained within the Service for future reuse. The Framework object may exist across multiple executions, and the provider instance may be reused.
Provider instances may also be used by multiple threads.
Your provider class should not use any instance fields and should be careful not to use un-threadsafe operations.
Some plugin methods return a "Result" interface which indicates the result status of the call to the plugin class. If there is an error, some plugins allow an Exception to be thrown or for the error to be included in the Result class. In both cases, there is a "FailureReason" that must be specified.
See the javadoc: FailureReason.
This can be any implementation of the FailureReason interface, and this object's toString()
method will be used to return the reason value (for example, it is passed to Error Handler steps in a Workflow as the "result.reason" string). The mechanism used internally is to provide an Enum implementation of the FailureReason interface, and to enumerate the possible reasons for failure within the enum.
You are encouraged to re-use existing FailureReasons as much as possible as they provide some basic failure causes. Existing classes:
To define a plugin that presents custom GUI configuration properties and/or uses Project/Framework level configuration, you need to provide a Description of your plugin. The Description defines metadata about the plugin, such as the display name and descriptive text, as well as the list of all configuration Properties that it supports.
There are several ways to declare your plugin's Description:
Collaborator interface
Implement the DescriptionBuilder.Collaborator interface in your plugin class, and it will be given an opportunity to perform actions on the Builder object before it finally constructs a Description.
Describable interface
If you want to build the Description object yourself, you can do so by implementing the Describable interface. Return a Description instance. You can construct one by using the DescriptionBuilder builder class.
Description Annotations
Newer plugin types support using java annotations to create a Description object. See Plugin Annotations.
Within a Description object you can define a set of Property objects, which represent the input properties for the plugin.
Some plugin types support using Java Annotations to define properties, see Plugin Annotations.
For the remaining plugin types, the Properties must be defined using the other interfaces described above, typically with the use of a PropertyBuilder.
Rendering Options
You can specify "rendering options" to affect the property being rendered in the Rundeck GUI. These affect Property type String only:
For more information see the options under Property Rendering options.
A set of constants for the supported rendering option keys and some values are provided in the StringRenderingConstants.
Script plugins can provide the same services as Java plugins, but they do so with a script that is invoked in an external system processes by the JVM.
These Services support Script Plugins:
Note, the ResourceFormatParser and ResourceFormatGenerator services do not support the Script Plugin type.
You must create a zip file with the following structure:
[name]-plugin.zip
\- [name]-plugin/ -- root directory of zip contents, same name as zip file
|- plugin.yaml -- plugin metadata file
\- contents/
|- ... -- script or resource files
\- ...
Here is an example:
$ unzip -l example-1.0-plugin.zip
Archive: example-1.0-plugin.zip
Length Date Time Name
-------- ---- ---- ----
0 04-12-11 11:31 example-1.0-plugin/
0 04-11-11 15:31 example-1.0-plugin/contents/
2142 04-11-11 15:31 example-1.0-plugin/contents/script1.sh
1591 04-11-11 13:10 example-1.0-plugin/contents/script2.sh
576 04-12-11 10:58 example-1.0-plugin/plugin.yaml
-------- -------
4309 5 files
The filename of the plugin zip must end with "-plugin.zip" to be recognized as a plugin archive. The zip must contain a top-level directory with the same base name as the zip file (sans ".zip").
The file plugin.yaml
must have this structure:
# yaml plugin metadata
name: plugin name
version: plugin version
rundeckPluginVersion: 1.0
author: author name
date: release date
providers:
- name: provider
service: service name
plugin-type: script
script-interpreter: [interpreter]
script-file: [script file name]
script-args: [script file args]
The main metadata that is required:
name
- name for the pluginversion
- version number of the pluginrundeckPluginVersion
- Rundeck Plugin type version, currently "1.0"providers
- list of provider metadata mapsThese are optional:
author
- optional author infodate
- optional release date infoThis provides the necessary metadata about the plugin, including one or more entries in the providers
list to declare those providers defined in the plugin.
Required provider entries:
name
- provider nameservice
- service name, one of these valid services:
NodeExecutor
FileCopier
ResourceModelSource
WorkflowNodeStep
RemoteScriptNodeStep
plugin-type
- must be "script" currently.script-file
- must be the name of a file relative to the contents
directoryFor ResourceModelSource
service, this additional entry is required:
resource-format
- Must be the name of one of the supported [Resource Model Document Formats].Optional entries:
script-interpreter
- A system command that should be used to execute the script. This can be a single binary path, e.g. /bin/bash
, or include any args to the command, such as /bin/bash -c
.script-args
- the arguments to use when executing the script file.interpreter-args-quoted
- true/false - (default false). If true, the execution will be done by passing the file and args as a single argument to the interpreter: ${interpreter} "${file} ${arg1} ${arg2}..."
. If false, the execution will be done by passing the file and args as separate arguments: ${interpreter} ${file} ${arg1} ${arg2}...
config
- a Map defining custom plugin properties (see below.)Custom plugin properties are supported in script-based plugins for these plugin types:
ResourceModelSource
NodeExecutor
FileCopier
WorkflowNodeStep
RemoteScriptNodeStep
You can use the metadata entries to declare properties about your plugin.
Create a config
entry in each provider definition, containing a sequence of map entries for each configuration property you want to define. In the map entry include:
type
- The type of property. Must be one of:
String
Boolean
value must be "true" or "false"Integer
Long
Select
must be on of a set of valuesFreeSelect
may be one of a set of valuesname
- Name to identify the propertytitle
- Title to display in the GUI (optional)description
- Description to display in the GUI (optional)required
- (true/false) if true, require a non-empty value (optional)default
- A default value to use (optional)values
- A comma-separated list of values to use for Select or FreeSelect. Required for Select/FreeSelect.scope
- Resolution scope for the property. Default: "Instance".renderingOptions
- A Map containing a definition of rendering option keys/values. (See Property Rendering Options below.)When your script is invoked, each configuration property defined for the plugin will be set as RD_CONFIG_[PROPERTY]
variables passed to your script (see below). "PROPERTY" refers to your plugin property name in uppercase. A plugin property named "foo" will translate to the shell variable RD_CONFIG_FOO
.
See Property Scopes below.
Here is an example:
# yaml plugin metadata
name: plugin name
version: plugin version
rundeckPluginVersion: 1.0
author: author name
date: release date
providers:
- name: provider
service: service name
plugin-type: script
script-interpreter: [interpreter]
script-file: [script file name]
script-args: [script file args]
config:
- name: myprop
title: My Property
type: String
required: false
description: "A custom property"
scope: Instance
renderingOptions:
instance-scope-node-attribute: "my-prop"
- name: myprop2
title: Another Property
type: Integer
required: true
description: "Must be present"
default: '123'
scope: Framework
When the provider is used for node execution or file copying, the script file, interpreter, and args are combined into a commandline executed by the system in this pattern:
[interpreter] [filename] [args...]
If the interpreter is not specified, then the script file is executed directly, and that means it must be acceptable by the system to be executed directly (include any necessary #!
line, etc).
script-args
can contain data-context properties such as ${node.name}
. Additionally, the specific Service will provide some additional context properties that can be used:
${exec.command}
containing the command to be executed${file-copy.file}
containing the local path to the file that needs to be copied, and ${file-copy.destination}
containing the remote destination path that is requested, if available.${config.KEY}
for each configuration property KEY that is defined.All script-plugins will also be provided with these context entries:
rundeck.base
- base directory of the Rundeck installationrundeck.project
- project nameplugin.base
- base directory of the expanded 'contents' dir of the pluginplugin.file
- the plugin file itselfplugin.scriptfile
- the path to the script file being executed for the pluginplugin.vardir
- var dir the plugin can use for caching data, local to the projectplugin.tmpdir
- temp dir the plugin can useIn addition, all of the data-context properties that are available in the script-args
are provided as environment variables to the script or interpreter when it is executed.
Environment variables are generated in all-caps with this format:
RD_[KEY]_[NAME]
The KEY
and NAME
are the same as ${key.name}
. Any characters in the key or name that are not valid Bash shell variable characters are replaced with underscore '_'.
Examples:
${node.name}
becomes $RD_NODE_NAME
${node.some-attribute}
becomes $RD_NODE_SOME_ATTRIBUTE
${exec.command}
becomes $RD_EXEC_COMMAND
${file-copy.file}
becomes $RD_FILE_COPY_FILE
The specific service has expectations about the way your provider script behaves.
NodeExecutor
All output to STDOUT
/STDERR
will be captured for the job's output.
FileCopier
The first line of output of STDOUT
MUST be the filepath of the file copied to the target node. Other output is ignored. All output to STDERR
will be captured for the job's output.
ResourceModelSource
All output on STDOUT
will be captured and passed to a ResourceFormatParser
for the specified resource-format
to create the Node definitions.
The scope
determines how the value of the property is resolved.
These are the available scopes and how the property values can be resolved:
Framework
- Only framework propertiesProjectOnly
- Only Project propertiesProject
- Project and Framework propertiesInstanceOnly
- Only instance propertiesInstance
- Instance and all earlier levelsWhen resolving a property in a Project or Framework scope, the following properties will be searched:
$RDECK_BASE/etc/framework.properties
framework.plugin.[ServiceName].[providerName].[propertyname]
$RDECK_BASE/projects/[ProjectName]/etc/project.properties
project.plugin.[ServiceName].[providerName].[propertyname]
Instance scope refers to the configuration for a plugin "instance". For different plugin types, due to the differences between how they are configured, this scope is resolved in different ways.
ResourceModelSource
:Resolved for each indexed source via the configuration in the project.properties
file.
NodeExecutor
, and FileCopier
:Custom properties are only supported in Script-based plugins. The instance scoped-property values are only loaded via Node attributes at execution time, based on the mapping provided in the renderingOptions
. See Node services instance scope.
Resolved from the step configuration defined in the workflow.
Notification
:Resolved from the notification trigger configuration defined in the workflow.
For script-based NodeExecutor
and FileCopier
plugins, Instance-scope properties can be defined to retrieve values from the Node's attributes at execution time.
Define the renderingOptions
property entry, and add a instance-scope-node-attribute
key:
config:
- name: myprop
title: My Property
type: String
required: false
description: "A custom property"
scope: Instance
renderingOptions:
instance-scope-node-attribute: "my-prop"
When resolving the property value at execution time the node attribute "my-prop" will be used for the value (if present), before resolving other allowed scopes.
Rendering options define special attributes of a property, which can be used to declare how it is shown in the GUI, or how the value is loaded at resolution time.
Available rendering option keys:
displayType
, values:
SINGLE_LINE
- display input as a single text field.MULTI_LINE
- display input as a multi-line text area.PASSWORD
- display input as a password fieldSTATIC_TEXT
- display static text without an input field, using the default value as the textinstance-scope-node-attribute
NodeExecutor
and FileCopier
only.selectionAccessor
, values:
STORAGE_PATH
- display an additional input to select a Storage Path string from Rundeck's Key Storage Facility.storage-path-root
STORAGE_PATH
.storage-file-meta-filter
STORAGE_PATH
. Filt string format: metadatakey=value
, e.g. Rundeck-key-type=private
.valueConversion
defines a way to convert a the value of the resolved property. Allowed values:
STORAGE_PATH_AUTOMATIC_READ
- automatically loads the storage contents from the given Storage Path, replacing the path with the loaded file contents as a String. E.g. can be used to load a Password file contents.PRIVATE_DATA_CONTEXT
- automatically read a value from the Private data context, which contains Secure Authentication Job Option values. E.g. With this conversion enabled, a config value of "option.mypassword" would be replaced with the value of a secure authentication job option named "mypassword".valueConversionFailure
can be used to indicate that if the Private data context, or Storage path data is not present, then the config data key should be removed. Allowed values:
remove
- remove the original config property value if the conversion is not successfulstaticTextContentType
if displayType is STATIC_TEXT
, the content type for the defaultValue text. values:
text/html
render as sanitized HTMLtext/x-markdown
convert markdown text to sanitized HTMLgroupName
specifies a group that the input field belongs to. If not specified the field will be in the primary, unnamed group. If specified, all fields with the same groupName
value will be displayed in a common area under the group name.grouping
allowed value: secondary
, indicates that the specified groupName
should be shown in a collapsed state if no input values in that group have been set. If no groupName
is set, then the field will be displayed under a group with a heading of "More".