Skip to main content
DevOps icon showing cogs

This post is from the XebiaLabs blog and has not been updated since the original publish date.

Last Updated Aug 10, 2015 — DevOps Expert

Creating XL Release Tasks Dynamically Using Jython API

DevOps

This blog post caters to users who have a good understanding of Tasks in XL Releases and also know how to code in python. It talks about how you can make the best use of enhanced Jython API that has been available since XL Release 4.6.0 to dynamically generate tasks in a phase inside a release during runtime. Jython API can be used in two different ways in XL Release.

  1. It can be injected as a code snippet into a Script Task and will then be executed when the release execution reaches the Task. This can be thought of as a macro for those who have used MS Excel/Project in the past along with VB Macros.  The only downside of putting code snippets inside a script task is that its fragile, can be easily broken with a single keystroke by mistake and doesn't give nice appearance with fancy user inputs
  2. The second more fancier way is to properly wrap around your code snippet in a Custom Task and expose the input and output fields in UI Text fields. This provides a better way to encapsulate the code snippet and its much more stable since the inline code logic can't be tampered with

Here's a Jython code snippet to create both Simple and Custom script tasks with XL Releases. The following two code snippets when directly pasted into a script Task separately and executed would leave to creation of a variety of new tasks in the same phase namely a manual task, notification task, script task, deployit task, webhook and jenkins Simple Tasks with Jython

import sys, string, time
import com.xhaus.jyson.JysonCodec as json
from com.xebialabs.xlrelease.domain import Task
from com.xebialabs.deployit.plugin.api.reflect import Type
from java.text import SimpleDateFormat
def createSimpleTask(phaseId, taskTypeValue, title, propertyMap):
    parenttaskType = Type.valueOf(taskTypeValue)
    parentTask = parenttaskType.descriptor.newInstance("nonamerequired")
    parentTask.setTitle(title)
    sdf = SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
    for item in propertyMap:
        if item.lower().find("date") > -1:
            if propertyMap[item] is not None and len(propertyMap[item]) != 0:
                parentTask.setProperty(item,sdf.parse(propertyMap[item]))
        else:
            parentTask.setProperty(item,propertyMap[item])
    taskApi.addTask(phaseId,parentTask)
createSimpleTask(phase.id,"xlrelease.Task", "this is cool", {'description':'coolio'})
createSimpleTask(phase.id,"xlrelease.NotificationTask", "this is the title", {'description':'this is the description'})
createSimpleTask(phase.id,"xlrelease.ScriptTask", "this is the title", {'description':'this is the description'})
createSimpleTask(phase.id,"xlrelease.DeployitTask", "this is the title", {'description':'this is the description','server':'localhost'})

Custom Script Tasks with Jython

import sys, string, time
import com.xhaus.jyson.JysonCodec as json
from com.xebialabs.xlrelease.domain import Task
from com.xebialabs.deployit.plugin.api.reflect import Type
from java.text import SimpleDateFormat
def createScriptBasedTask(phaseId,taskTypeValue,title,precondition, propertyMap):
    parenttaskType = Type.valueOf("xlrelease.CustomScriptTask")
    parentTask = parenttaskType.descriptor.newInstance("nonamerequired")
    parentTask.setTitle(title)
    childTaskType = Type.valueOf(taskTypeValue)
    childTask = childTaskType.descriptor.newInstance("nonamerequired")
    for item in propertyMap:
        childTask.setProperty(item,propertyMap[item])
    parentTask.setPythonScript(childTask)
    parentTask.setPrecondition(precondition)
    taskApi.addTask(phaseId,parentTask)
createScriptBasedTask(phase.id,"webhook.JsonWebhook", "this is the title",None,{"URL":"http://myurl", "method":"PUT", "body":"{key1:val1,key2:'value is 2'}", "username": "user", "password":"pass"})
createScriptBasedTask(phase.id,"webhook.XmlWebhook", "this is the title",None,{})
createScriptBasedTask(phase.id,"jenkins.Build", "this is the title",None,{})

Now the next cool thing would be to encapsulate these into Custom Task wrapper so that we can only expose the input and outputs steps:

  • Lets think of a use case. So my use case for this blog is such that i want to be able to generate multiple XL Deploy Deployment Tasks based on a list of Application vs Environment map list. User should be able to invoke it either through the UI or REST API.
  • So now let's stop our XL Release server and go to the XLR_HOME/ext directory.
  • In there we'll edit the synthetic.xml to add a new custom task type.
<type type="mytype.GenerateDeployments" extends="xlrelease.PythonScript" >
        <property name="server" label="XL Deploy Server Reference" category="input" />
        <property name="targetPhase" label="Target Phase to add Deployments" category="input" />
        <property name="deploymentMap" label="Deployment Map" category="input" size="large" /> 
</type>
  • Then create a new folder called mytype under XLR_HOME/ext directory
  • Under mytype folder, create a new python script called GenerateDeployments.py
import sys, string, time
import com.xhaus.jyson.JysonCodec as json
from com.xebialabs.xlrelease.domain import Task
from com.xebialabs.deployit.plugin.api.reflect import Type
from java.text import SimpleDateFormat
def createSimpleTask(phaseId, taskTypeValue, title, propertyMap):
    parenttaskType = Type.valueOf(taskTypeValue)
    parentTask = parenttaskType.descriptor.newInstance("nonamerequired")
    parentTask.setTitle(title)
    sdf = SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
    for item in propertyMap:
        if item.lower().find("date") > -1:
            if propertyMap[item] is not None and len(propertyMap[item]) != 0:
                parentTask.setProperty(item,sdf.parse(propertyMap[item]))
        else:
            parentTask.setProperty(item,propertyMap[item])
    taskApi.addTask(phaseId,parentTask)
serverId = "Configuration/Deployit/" + str(server)
deploymentList = deploymentMap.split(",")
phaseList = phaseApi.searchPhasesByTitle(targetPhase,release.id)
if len(phaseList) == 1:
 for item in deploymentList:
  itemSplit = item.split(":")
  deploymentPackage = itemSplit[0]
  environment = itemSplit[1]
  createSimpleTask(phaseList[0].id,"xlrelease.DeployitTask", "Deployment of %s to %s"%(deploymentPackage,environment), {'description':"Deployment of %s to %s"%(deploymentPackage,environment),'server':serverId,'deploymentPackage':deploymentPackage,'environment':environment})
  • Now restart the server and login using browser.
  • Create a new Template called MorningReleaseTemplateScreen Shot 2015-08-10 at 3.52.09 PM
  • In the properties, make sure to set Script User and Password to something like admin/admin or a user  that has permissions to execute scripts
  • Create two phase prepare and deploy
  • Create first task in prepare phase as mytype.GenerateDeployments
  • Create Second task as a manual Task
  • Create another manual Task in Deploy Phase
  • Set the following input properties in the custom Task

Screen Shot 2015-08-10 at 3.57.41 PM

  • XL Deploy Server Reference to a target XLD Server name under configuration
  • Target Phase to add Deployments to a future phase name. Deploy in this case
  • DeploymentMap to a variable for Now called ${deploymentMap}

Now our Template is ready to be kicked off remotely. I am using a firefox REST Client to trigger a release. Here's how its done

  • From the browser in which the XLR with template is open, copy the last part of the address bar which is like this eg, Release8717722
  • Open a new REST Client window and type the following and submit Send
  • URL :http://localhost:5516/api/v1/templates/Applications/Release8717722/start
  • Method : POST
  • Headers : authentication, content-type:application/json, Accepts:application/json
  • Body :
    {
    "releaseTitle":"Release123",
    "releaseVariables":{"${deploymentMap}":"App/1.0:DEV,App/2.0:QA"}
    }

It looks like this: Screen Shot 2015-08-10 at 4.03.01 PM The Response in XL Release is that it created a new release and triggered it. It also ended up creating deployment tasks dynamically which were initially not part of the template. Screen Shot 2015-08-10 at 4.28.42 PM

 

More from the Blog

View more
Ascension Launch Banner
Apr 26, 2022

Get ready for peak performance with Digital.ai’s newest AI-Powered DevOps Platform Ascension Release

DevOps
Today, Digital.ai is excited to announce our latest AI-Powered DevOps ...
Read More
Jan 24, 2022

Digital.ai Value Stream Delivery for SAFe®: The key to amazing business outcomes

DevOps
The Scaled Agile Framework (SAFe) is the world’s leading framework for ...
Read More
Dec 09, 2021

How SaaS and cloud-based solutions helped the U.S. Department of Veterans Affairs achieve digital transformation

DevOps
Modernizing legacy systems was an ongoing goal for the U.S. Department ...
Read More
Nov 29, 2021

Increase velocity and reduce risk with AI and machine learning

DevOps
Artificial Intelligence (AI) and machine learning (ML) have proven use ...
Read More
Contact Us