Plug-in tasks
Plug-in steps can interact with the plug-in tasks that are used in deployment plans.
Plug-in steps can respond to notification-type events in external tools and provide data from
those tools to IBM® UrbanCode™
Release plug-in
tasks. For example, you might create plug-ins that integrate with ticketing systems and email
servers, and then have the external tools provide data to plug-in tasks in deployment plans. To
enable a plug-in to work with plug-in tasks, you modify the steps in the
plugin.xml file. To work with plug-in tasks, a plug-in step extends the
PLUGIN_TASK
class.
The plug-in step is responsible to set the task status after execution. The step can wait for the process execution to complete or provide a call-back URL in order for the external tool to update the task status later. The plug-in step can add information about the task execution to the task's Comments tab.
Example of a plug-in that can be used with plug-in tasks
plugin.xml
file illustrates how to create plug-in
steps that can be used by plug-in tasks. The step extends PLUGIN_TASK
and defines
several properties. Users can set the property values in the plug-in task. The step sets the task's
status in the post-processing
element.<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plugin xmlns="http://www.urbancode.com/PluginXMLSchema_v1" xmlns:server="http://www.urbancode.com/PluginServerXMLSchema_v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<header>
<identifier id="com.urbancode.plugin.demo" name="Demo Plugin" version="1"/>
<plugin-type>Connector</plugin-type>
<description>
Demo Plugin
</description>
<tag>tools/demo</tag>
</header>
<!-- extends="PLUGIN_TASK" will register that step as a task when creating an integration provider with that plugin-->
<step-type name="ExecuteTask" displayed="true" extends="PLUGIN_TASK">
<description>Execute Task</description>
<properties>
<property name="property1" required="true">
<property-ui type="textBox" description="Property 1" label="Plugin Property 1" />
</property>
<property name="property2" required="true">
<property-ui type="textBox" description="Property 2" label="Plugin Property 2" />
</property>
<property name="property3" required="true">
<property-ui type="textAreaBox" description="Property 3" label="Plugin Property 3"/>
</property>
</properties>
<post-processing>
<![CDATA[
if (properties.get("exitCode") != 0) {
properties.put(new java.lang.String("Status"), new java.lang.String("Failure"));
}
else {
properties.put("Status", "Success");
}
]]>
</post-processing>
<command program="${GROOVY_HOME}/bin/groovy">
<arg value="-cp"/>
<!-- ucr-plugin-util.jar is the plugin client for consuming UCR rest api-->
<arg path="classes:lib/commons-codec.jar:lib/CommonsUtil.jar:lib/ucr-plugin-util.jar:lib/commons-lang3.jar:lib/commons-lang.jar:lib/jettison-1.1.jar"/>
<arg file="ExecuteTask.groovy"/>
<arg file="${PLUGIN_INPUT_PROPS}"/>
<arg file="${PLUGIN_OUTPUT_PROPS}"/>
</command>
</step-type>
</plugin>
Groovy file example
The plug-in framework passes the entire context as a JsonObject,
extraProperties
, that can be parsed into an object. After the plug-in step parses
the JsonObject, it has access to every element of the plug-in task, such as
callBackUrl
, taskUrl
, and UserId
.
The following code example, ExecuteTask.groovy
, illustrates how to parse the
extraProperties
object, and add comments to a plug-in task.
#!/usr/bin/env groovy
/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Release
* (c) Copyright IBM Corporation 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
import static com.urbancode.release.rest.framework.Clients.version;
import static com.urbancode.release.rest.models.internal.InternalClients.status;
import static com.urbancode.release.rest.models.internal.InternalClients.versionStatus;
import static com.urbancode.release.rest.models.internal.InternalClients.version;
import com.urbancode.release.rest.models.internal.TaskExecution;
import com.urbancode.air.*
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import com.urbancode.release.rest.models.Version;
import com.urbancode.release.rest.models.internal.ApprovalSet;
import com.urbancode.release.rest.models.internal.ScheduledDeployment;
import com.urbancode.release.rest.models.internal.PluginIntegrationProvider;
import com.urbancode.release.rest.models.internal.Status;
import com.urbancode.release.rest.models.internal.Comment;
import com.urbancode.release.rest.models.internal.VersionStatus;
import com.urbancode.release.rest.framework.Clients;
final def workDir = new File('.').canonicalFile
def apTool = new AirPluginTool(this.args[0], this.args[1]);
def props = apTool.getStepProperties();
//We launch the integration here
def executor = new TaskExecutor (props)
//We authenticate first
executor.releaseAuthentication();
//We then execute the task
executor.run ()
public class TaskExecutor {
//Used to parse JsonObjects
def slurper;
def integrationProviderId
//Used for authentication
def releaseToken;
def serverUrl;
//JsonObject that will contain context properties
def extraProperties;
//Properties defined in the UI for the integration provider
def uiProperty1 = "";
def uiProperty2 = "";
def uiProperty3 = "";
//The task object
def task;
//Main constructor
TaskExecutor (props) {
this.integrationProviderId = props['releaseIntegrationProvider'];
this.releaseToken = props['releaseToken'];
this.serverUrl = props['releaseServerUrl'];
//Context properties
this.extraProperties = props['extraProperties'];
//UI Integration properties
this.uiProperty1 = props['property1'];
this.uiProperty2 = props['property2'];
this.uiProperty3 = props['property3'];
this.extraProperties = props['extraProperties'];
//We initialize the Json parser
this.slurper = new groovy.json.JsonSlurper()
}
//--------------------------------------------------------------
//Authentication with Release
def releaseAuthentication () {
Clients.loginWithToken(serverUrl, releaseToken);
}
//--------------------------------------------------------------
def run () {
//Lets display the UI input properties
addLogSection ("Global Properties set on the integration provider");
log ("Property 1: "+this.uiProperty1);
log ("Property 2: "+this.uiProperty2);
log ("Property 3: "+this.uiProperty3);
//We load the context properties
//If ran from the integration provider page and not a scheduled deployment the extraProperties object my be null
if (extraProperties) {
log ("Task ran from a scheduled deployment");
def contextProperties = slurper.parseText(extraProperties)
//The context properties will provide the task object as well as the plugin properties set on it in the plan
addLogSection ("Task information");
//callBackUrl
log ("Call Back URL: "+contextProperties.callBackUrl);
//Direct link in UCR for that task
log ("Direct link to that task: "+contextProperties.taskUrl);
//User who executed the task
log ("User Id (Admin for automated tasks): "+contextProperties.userId);
//Task properties
if (contextProperties.task != null) {
//We need the ucr task id so we can handle that UCR object later
def urcTaskId = contextProperties.task.id
task = new TaskExecution()
task.id(urcTaskId)
log ("Task Id: "+urcTaskId);
log ("Task Name: "+contextProperties.task.name);
log ("Task Description: "+contextProperties.task.description);
log ("Scheduled Deployment Id: "+contextProperties.task.scheduledDeploymentId);
log ("Segment Id: "+contextProperties.task.segmentExecution.id);
log ("Segment Name: "+contextProperties.task.segmentExecution.name);
log ("Only Changed Versions: "+contextProperties.task.onlyChangedVersions);
addLogSection ("Application Info");
if (contextProperties.task.application != null) {
log ("Application UCR Id: "+contextProperties.task.application.id);
log ("Application External Id: "+contextProperties.task.application.externalId);
log ("Application Name: "+contextProperties.task.application.name);
addLogSection ("Version Info");
if (contextProperties.task.version != null) {
log ("Version UCR Id: "+contextProperties.task.version.id);
log ("Version External Id: "+contextProperties.task.version.externalId);
log ("Version Name: "+contextProperties.task.version.name);
//Version Statuses
if (contextProperties.task.version.versionStatuses != null) {
contextProperties.task.version.versionStatuses.each {
versionStatus -> log ("Status: "+versionStatus.status.name+" added by "+versionStatus.createdByUser.actualName+" on "+new Date(versionStatus.dateCreated));
}
}
}
}
addLogSection ("Target Info");
if (contextProperties.task.targets != null) {
contextProperties.task.targets.each {
target -> log ("Target Name: "+target.name);
log ("Target UCR Id: "+target.id);
log ("Target External Id: "+target.externalId);
}
}
addLogSection ("Approval information");
//To get more about the deployment we need to load the object
def sdClient = new ScheduledDeployment();
sdClient.format("detail");
def sd = sdClient.id(contextProperties.task.scheduledDeploymentId).get();
def approvalSet = sd.approval;
approvalSet.tasks.each {
approvalItem -> log ("Approval: "+approvalItem.name+" by "+approvalItem.executorRole.name+" User: "+ approvalItem.userName);
log ("Approval ID: "+approvalItem.id);
}
}
addLogSection ("Task plugin properties");
if (contextProperties.task.pluginProperties != null) {
log ("Plugin properties input from the task UI "+contextProperties.task.pluginProperties);
}
////////////////////////////////////////////////////////////////////////////////////////
// WHATEVER THE PLUGIN TASK SHOULD BE DOING
//Comments can be added to the task
new Comment().userId(contextProperties.userId).task(task).comment("Here is a comment posted during the execution of the task").post();
//Once done the status of the tasks should be updated
//task.complete() or task.fail()
task.complete();
////////////////////////////////////////////////////////////////////////////////////////
}
else {
log ("Task ran from the integration provider page");
}
}
//--------------------------------------------------------------
def log (logText) {
println (logText)
}
//--------------------------------------------------------------
def addLogSection (sectionTitle) {
println ("")
println ("------------------------------------------------------------------")
println ("<b><u>"+sectionTitle+"</u></b>")
println ("")
}
}