XL Deploy Variables Demystified : Part 1

A lot of new people starting on XL Deploy always get these questions in mind,

“How do it get to know what variables i could use in my script ?”
“What variable are exposed by Freemarker ?”
“Maybe ${deployed.id} might work to give me the right value?”
Finally to answer all those questions, here’s a freemarker snippet which exposes all the variables ( I mean, not all.. that will be too much ) that can be used in the script to derive values from types, deployeds, containers and what not.
At the end of the day, its all java objects embedded in freemarker context so there’s much more information like the methods exposed in there apart from just the properties. So without wasting any further time, here the code..

<#-- This function recursively digs the values of variables exposed for use in script -->
<#function dig obj level depth captureMethod>
	<#assign result =[] >
	<#assign result = result + ["Complex Object: " + obj] >
	<#assign prefix=".vars.${obj}" >
        <#if prefix?eval??>
	<#if prefix?eval?is_hash_ex >
		<#list prefix?eval?keys as key>
			<#assign basevar="${obj}.${key}" >
			<#attempt>
				<#assign valuevar="${basevar}"?eval >
				<#if valuevar?is_method >
					<#if captureMethod >
						<#assign result = result + [r"METHOD: ${" + basevar + r"(...)}"] >
					</#if>
				<#else>
					<#if valuevar?is_string || valuevar?is_boolean || valuevar?is_number>
						<#assign result = result + [r"PROPERTY: ${" + basevar + r"}  ||  VALUE: " + valuevar?string] >
					</#if>
                                        <#if valuevar?is_enumerable>
                                                <#assign result = result + [r"PROPERTY: ${" + basevar + r"} || TYPE : LIST "] >
                                        </#if>
                                        <#if valuevar?is_hash_ex >
						<#if level < depth >
							<#assign result = result + dig(basevar,level + 1,depth, captureMethod) >
						</#if>
					</#if>
				</#if>
			<#recover>
				<#assign result = result + [r"PROPERTY: ${" + basevar + r"}  ||  VALUE: (UNDEFINED/UNRESOLVED) "]>
			</#attempt>
		</#list>
	<#else>
		<#assign result = result + [" Cannot be parsed as {" + obj + "} is not a hash or simple property"] >
	</#if>
        </#if>
	<#return result>
</#function>
<#-- Macro initiates variable digging and then prints them -->
<#macro variableList depth captureMethod>
	<#assign result=[]>
	<#list .data_model?keys as keyouter>
		<#assign result = result + dig(keyouter, 0, depth, captureMethod)>
	</#list>
VARIABLE LISTING TILL DEPTH: ${depth}
=====================================
	<#list result as item>
		${item}
	</#list>
</#macro>
<#-- Specify the depth till which to explore -->
<@variableList depth=0 captureMethod=false/>

This code snippet can be copied into a script that is used with XLD artifacts as a createScript. It can be used with generic plugin and with XL-rules and any other plugins that have execution scripts specified such that they show up under plan analyzer and can be opened by double clicking eyeball icon.
This script would expose all the variables and the properties with them. It works recursively so you can specify the what level of depth you want to go to.
eg. base depth is 0. so setting depth=1, will show properties like deployed.container.os.
It prints the value of literal properties, also lists the methods available with objects is captureMethod=true.
For generic plugin, with captureMethod=false, output shows up like this.

VARIABLE LISTING TILL DEPTH: 0
=====================================
 Complex Object: deployed
 PROPERTY: ${deployed.createScript} || VALUE: script/osscript.sh.ftl
 PROPERTY: ${deployed.remoteWorkingDirectoryPath} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.inspectScript} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.planOperation} || VALUE: CREATE
 PROPERTY: ${deployed.modifyScript} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.noopOrder} || VALUE: 50
 PROPERTY: ${deployed.modifyOrder} || VALUE: 50
 PROPERTY: ${deployed.noopScript} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.$ciAttributes} || VALUE: com.xebialabs.XL Deploy.plugin.api.udm.CiAttributes@42472788
 PROPERTY: ${deployed.noopVerb} || VALUE: Modify
 PROPERTY: ${deployed.destroyVerb} || VALUE: Destroy
 PROPERTY: ${deployed.retainRemoteWorkingDirectory} || VALUE: false
 PROPERTY: ${deployed.destroyScript} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.createVerb} || VALUE: Create
 PROPERTY: ${deployed.type} || VALUE: test.scripttype1
 PROPERTY: ${deployed.restartRequired} || VALUE: false
 PROPERTY: ${deployed.class} || VALUE: class com.xebialabs.XL Deploy.plugin.generic.deployed.ExecutedScript
 PROPERTY: ${deployed.createOrder} || VALUE: 50
 PROPERTY: ${deployed.modifyVerb} || VALUE: Modify
 PROPERTY: ${deployed.id} || VALUE: Infrastructure/local/newtype
 PROPERTY: ${deployed.name} || VALUE: newtype
 PROPERTY: ${deployed.destroyOrder} || VALUE: 40
 PROPERTY: ${deployed.$token} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${deployed.restartRequiredForNoop} || VALUE: false
 Complex Object: step
 PROPERTY: ${step.uploadedArtifactPath} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${step.hostFileSeparator} || VALUE: /
 PROPERTY: ${step.localConnection} || VALUE: local:
 PROPERTY: ${step.retainRemoteWorkingDirOnCompletion} || VALUE: false
 PROPERTY: ${step.hostLineSeparator} || VALUE:
PROPERTY: ${step.scriptTemplatePath} || VALUE: script/osscript.sh.ftl
 PROPERTY: ${step.class} || VALUE: class com.xebialabs.XL Deploy.plugin.generic.step.ScriptExecutionStep
 PROPERTY: ${step.preview} || VALUE: com.xebialabs.XL Deploy.plugin.api.flow.Preview@552fae2e
 PROPERTY: ${step.remoteWorkingDirPath} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${step.remoteConnection} || VALUE: local:
 PROPERTY: ${step.scriptPath} || VALUE: script/osscript.sh.ftl
 PROPERTY: ${step.artifact} || VALUE: (UNDEFINED/UNRESOLVED)
 PROPERTY: ${step.remoteWorkingDirectory} || VALUE: local:/var/folders/mf/wyk69xfn6_nfg04s6vt4gjrw0000gn/T/ot-20150101T191228243/generic_plugin.tmp
 Complex Object: statics
 Cannot be parsed as {statics} is not a hash or simple property

For XL-Rules, with captureMethod=true, output shows like this.

VARIABLE LISTING TILL DEPTH: 0
=====================================
 Complex Object: deployed
 METHOD: ${deployed.putSyntheticProperty(...)}
 METHOD: ${deployed.hasSyntheticProperty(...)}
 METHOD: ${deployed.get$ciAttributes(...)}
 METHOD: ${deployed.getSyntheticProperties(...)}
 METHOD: ${deployed.hashCode(...)}
 PROPERTY: ${deployed.type} || VALUE: test.scripttype
 METHOD: ${deployed.putSyntheticProperties(...)}
 METHOD: ${deployed.setDeployable(...)}
 PROPERTY: ${deployed.id} || VALUE: Infrastructure/local/osscript
 METHOD: ${deployed.getType(...)}
 METHOD: ${deployed.setContainer(...)}
 METHOD: ${deployed.getSyntheticProperty(...)}
 PROPERTY: ${deployed.name} || VALUE: osscript
 METHOD: ${deployed.setId(...)}
 METHOD: ${deployed.get$token(...)}
 METHOD: ${deployed.getId(...)}
 METHOD: ${deployed.getClass(...)}
 METHOD: ${deployed.getContainer(...)}
 METHOD: ${deployed.hasProperty(...)}
 METHOD: ${deployed.equals(...)}
 METHOD: ${deployed.setType(...)}
 PROPERTY: ${deployed.class} || VALUE: class com.xebialabs.XL Deploy.plugin.api.udm.base.BaseDeployed
 METHOD: ${deployed.compareTo(...)}
 PROPERTY: ${deployed.$token} || VALUE: (UNDEFINED/UNRESOLVED)
 METHOD: ${deployed.setProperty(...)}
 METHOD: ${deployed.set$token(...)}
 PROPERTY: ${deployed.$ciAttributes} || VALUE: com.xebialabs.XL Deploy.plugin.api.udm.CiAttributes@54707e93
 METHOD: ${deployed.setSyntheticProperties(...)}
 METHOD: ${deployed.getName(...)}
 METHOD: ${deployed.getProperty(...)}
 METHOD: ${deployed.set$ciAttributes(...)}
 METHOD: ${deployed.getDeployable(...)}
 METHOD: ${deployed.toString(...)}

Now this Freemarker snippet can be used wherever a free marker context is available. It gives you convenient output if you include it in a script that shows up in plan analyzer while planning for  deployment. 
NOTE: This won’t work for jython step in XL-rules since that doesn’t exposes the freemarker context
IMPORTANT :  If you use this with generic or another plugin except XL-rules, it will mostly include 3 top level variables that will be explored further. deployed, step and statics.
statics : is not explored since its not of type hash or a simple type
step : when this is being explored, you’ll see a lot of exceptions being generated in the log and it takes a lot of time but it would still show up in a while. Exception are generated while trying to find out values of certain properties who are unresolved at that point and throws exceptions. TIP: Prefer keeping depth=0 or depth=1 if you prefer to use it with plugins other than XL-rules.  Or if you’re just more interested in getting details of deployed, you can do this

  1. Comment out this block in the snippet
  2. 	<#-- <#list .data_model?keys as keyouter>
    		<#assign result = result + dig(keyouter, 0, depth, captureMethod)>
    	</#list> -->
  3. Replace the block with just this line.
  4. <#assign result = result + dig("deployed", 0, depth, captureMethod)>
  5. Then you can specify a higher depth.

A simple example to try this out would be

  • Create a new type in synthetic.xml
<!-- example with xl-rules -->
<type type="test.scripttype" deployable-type="test.scriptdeployable" extends="udm.BaseDeployed" container-type="overthere.LocalHost">
  <generate-deployable type="test.scriptdeployable" extends="udm.BaseDeployable" />
</type>
<!-- example with generic plugin -->
<type type="test.scripttype1" deployable-type="test.scriptdeployable1" extends="generic.ExecutedScript" >        <generate-deployable type="test.scriptdeployable1" extends="generic.Resource" />
  <property name="createScript" default="script/osscript.sh.ftl" />
</type>
  • Create a new rule in xl-rules.xml
<rule name="scriptrule" scope="deployed">
 <conditions>
 <type>test.scripttype</type>
 <operation>CREATE</operation>
 <operation>MODIFY</operation>
 <operation>DESTROY</operation>
 </conditions>
 <steps>
  <os-script>
   <script>script/osscript.sh.ftl</script>
   <freemarker-context>
     <delta expression="true">delta</delta>
   </freemarker-context>
  </os-script>
 </steps>
</rule>
  • Create a folder script under XLDEPLOY_HOME/ext folder and create a new file called osscript.sh.ftl under that folder
  • Copy the above free marker snippet into that file
  • Restart server
  • Create new Application or include “test.scriptdeployable” type under existing package. This type will deploy on overthere.LocalHost by default. You may change the type to deploy on any container type.
  • Now start a new deployment and open the Plan Analyzer after mapping the type to container.
  • You should be able to see the output of variable list on double clicking the script.

Good luck demystifying the secrets of XL-Deploy.
If you want to have the code snippet instead of copying it from the blog, please refer it from this git repo : https://github.com/amitmohleji/XLDScripts
Happy New Year

Related Posts

Leave a Comment

Your email address will not be published. Required fields are marked *