Skip to main content

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 jenkinsSimple 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 PMThe 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
Feb 22, 2021

Reckoning DevOps’ role in the enterprise value stream

DevOps
If you’re a software or digital solutions company, you may use DevOps ...
Read More
Feb 10, 2021

Customer spotlight: Schneider avoiding bumps in the road with DevOps adoption

DevOps
Everyone wants to deliver software faster and more reliably. Companies ...
Read More
Jan 06, 2021

How testing automation can build a culture of QA while accelerating continuous delivery

DevOps
An organization’s level of automated test coverage is quickly emerging ...
Read More
Jul 30, 2020

Part 2: Is Technology Slowing Down Your Digital Transformation?

DevOps
In part one of this post, we shared insights from Andreas Prins’ webin ...
Read More
Contact Us