Tutorial for Using Rules
The rules system works with the Deploy planning phase. You can use XML or Jython to specify the steps that belong in a deployment plan and how the steps are configured.
This tutorial describes the process of using rules to create an new Deploy plugin.
The plugin actions:
- Waits a specific interval to start the deployment.
- Deploys and undeploys an artifact.
- Starts and stops a server.
Requirements for you to use this tutorial:
- You must know how to create CI types, as described in Customizing the Deploy type system
- Understand the concepts of Deploy planning, as described in Understanding Deploy architecture
- You are familiar with the objects and properties available in rules, as described in Objects and properties available in rules
The code provided in this tutorial is available as a demo plugin in the samples
directory of your Deploy installation.
Run the examples
To run the examples in this tutorial, no specific configuration or plugin is required.
Required files
To configure Deploy to use the examples in this tutorial, you must add or modify the following files in the ext
folder of the Deploy server:
synthetic.xml
, which contains the configuration item (CI) types that are defined.xl-rules.xml
, which contains the rules that are defined.
Place the additional scripts that you will define in the ext
folder.
The structure of the ext
folder after you finish this tutorial:
ext/
├── planning
│ └── start-stop-server.py
├── scripts
│ ├── deploy-artifact.bat.ftl
│ ├── deploy-artifact.sh.ftl
│ ├── undeploy-artifact.bat.ftl
│ ├── undeploy-artifact.sh.ftl
│ ├── start.bat.ftl
│ ├── start.sh.ftl
│ ├── stop.bat.ftl
│ └── stop.sh.ftl
├── synthetic.xml
└── xl-rules.xml
Restarting the server
After you change synthetic.xml
, you must restart the Deploy server.
By default, you must also restart the Deploy server after you change xl-rules.xml
and scripts in the ext
folder. You can configure Deploy to periodically rescan xl-rules.xml
and the ext
folder and apply any changes that it finds. Use this when you are developing a plugin. For more information, see Define a rule.
Error handling
If you make a mistake in the definition of synthetic.xml
or xl-rules.xml
, the server will return an error and may fail to start. Mistakes in the definition of scripts or expressions usually appear in the server log when you execute a deployment. For more information about troubleshooting the rules configuration, refer to Best practices for rules.
Deploy an artifact
Start with an application that contains one artifact and deploy the artifact to a server.
This part of the plugin:
- Uploads the artifact.
- Runs a script that installs the artifact in the correct location.
Add type definitions
In synthetic.xml
, add a type definition called example.ArtifactDeployed
for the application and add a container type named example.Server
:
<type type="example.Server" extends="udm.BaseContainer" description="Example server">
<property name="host" kind="ci" referenced-type="overthere.Host" as-containment="true"/>
<property name="home" description="Home directory for the server"/>
</type>
<type type="example.ArtifactDeployed" extends="udm.BaseDeployedArtifact" deployable-type="example.Artifact" container-type="example.Server" description="Artifact that can be deployed to an example server">
<generate-deployable type="example.Artifact" extends="udm.BaseDeployableFileArtifact"/>
</type>
Notes:
example.Server
extends fromudm.BaseContainer
and has ahost
property that refers to a CI of typeoverthere.Host
.- The deployed
example.ArtifactDeployed
extends fromudm.BaseDeployedArtifact
, which contains afile
property that the step uses. - The generated deployable
example.Artifact
extends fromudm.BaseDeployableFileArtifact
.
Define a rule for the artifact
To define an XML rule for the CI in xl-rules.xml
:
<rule name="example.ArtifactDeployed.CREATE_MODIFY" scope="deployed">
<conditions>
<type>example.ArtifactDeployed</type>
<operation>CREATE</operation>
<operation>MODIFY</operation>
</conditions>
<steps>
<os-script>
<script>scripts/deploy-artifact</script>
</os-script>
</steps>
</rule>
Notes:
- The
name
example.ArtifactDeployed.CREATE_MODIFY
identifies the rule in the system. Use a descriptive name that includes the name of the plugin and the type and operation the rule responds to. - The
scope
isdeployed
because this rule must contribute a step for every instance ofexample.ArtifactDeployed
in the deployment. - The rule matches on deltas with the operations
CREATE
andMODIFY
. Matching onCREATE
means that this rule will trigger when Deploy knows that the application must be created or deployed. Matching onMODIFY
means that the rule will contribute the same step to the plan upon modification. - The rule will create a step of type
os-script
, which can upload a file and run a templated script. Thescript
defines the path where the script template is located, relative to the plugin definition.
The following os-script
parameters are defined automatically:
- A description that includes the artifact name and the name of the server it will deploy to. You can optionally override the default description.
- The order, which is automatically set to 70, the default step order for artifacts. You can optionally override the default order.
- The target-host property receives a reference to the host of the container. The step will use this host to run the script.
Script to deploy the artifact
The FreeMarker variable for the deployed
object is automatically added to the freemarker-context
. The script can refer to properties of the deployed
object such as file location.
The script
parameter refers to scripts for Unix (deploy-artifact.sh.ftl
) and Windows (deploy-artifact.bat.ftl
). The step will select the correct script for the operating system that Deploy runs on. The scripts are actually script templates processed by FreeMarker. The template can access the variables passed in by the freemarker-context
parameter of the step.
The Unix script deploy-artifact.sh.ftl
contains:
echo "Deploying file on Unix"
mkdir -p ${deployed.container.home + "/context"}
cp ${deployed.file.path} ${deployed.container.home + "/context"}
echo "Done"
The script accesses the variable deployed
and uses it to find the location of the server installation and copy the file to the context
folder. The script also prints progress information in the step log.
Add a wait step
You can improve the plan with an additional step that waits a specific number of seconds before the actual deployment starts.
- While preparing the deployment, you can set the number of seconds to wait in the deployment properties.
- If you do not set a number, Deploy will not add a wait step to the plan.
Add a property to type definition
You must store the wait time in the deployment properties by adding the following property to udm.DeployedApplication
in synthetic.xml
:
<type-modification type="udm.DeployedApplication">
<property name="waitTime" kind="integer" label="Time in seconds to wait for starting the deployment" required="false"/>
</type-modification>
Define a rule to contribute a wait
step
Define a rule in xl-rules.xml
to contribute the wait
step to the plan:
<rule name="example.DeployedApplication.wait" scope="pre-plan">
<conditions>
<expression>specification.deployedOrPreviousApplication.waitTime is not None</expression>
</conditions>
<steps>
<wait>
<order>10</order>
<description expression="true">"Waiting %i seconds before starting the deployment" % specification.deployedOrPreviousApplication.waitTime</description>
<seconds expression="true">specification.deployedOrPreviousApplication.waitTime</seconds>
</wait>
</steps>
</rule>
Notes:
- The
scope
ispre-plan
. This means that:- The rule will only trigger once per deployment.
- The step that the rule contributes is added to the pre-plan, which is a sub-plan that Deploy prepends to the deployment plan.
- Only contribute a step to the plan when the user supplies a value for the wait time. There is a condition that checks if the
waitTime
property isnot None
. The expression must be defined in Jython. - If the condition holds, Deploy creates the step that is defined in the
steps
section and adds it to the plan. The step takes arguments that you specify in the rule definition:- The
order
is set to 10 to ensure that the rule will appear early in the plan. In this case, this will be the only step in the pre-plan, so theorder
value can be ignored. You must provide this required value for the wait step. The type oforder
is integer, so if it has a value that is not an integer, planning will fail.description
is a dynamically constructed string that describes what the step will do. Providing a description is optional. If you do not provide one, Deploy will use a default description.expression="true"
means that the definition will be evaluated by Jython and the resulting value will be passed to the step. This is required because the definition contains a dynamically constructed string.
- The
waitTime
value is retrieved from theDeployedApplication
and passed to the step. You can access theDeployedApplication
through thespecification
anddeployedOrPreviousApplication
. This automatically selects the correct deployed, which means that this step will work for aCREATE
orDESTROY
operation.
- The
For more information about the wait
step, see Steps Reference.
Test the deployment rules
To test the rules that you created:
- Install Deploy and the scripts as described in How to run the examples.
- Under Applications in the left pane, create an application that contains a deployable of type
example.Artifact
. Upload a dummy file when creating the deployable CI. - Under Infrastructure, create a host of type
overthere.LocalHost
and a container of typeexample.Server
. Set the home directory ofexample.Server
to a temporary location. - Under Environments, create an environment that contains the
example.Server
container. - Start a new deployment of the application to the environment. When preparing the deployment, click Deployment Properties and enter a wait time. If you do not provide a value, the wait step will not appear in the plan.
-
Click Modify Plan or Deploy. Deploy will create the following deployment plan:
-
Execute the plan. Check that the steps are succesful.
-
Verify that there is a
context
folder in the directory that you set as the home directory ofexample.Server
, and verify that the artifact was copied to it.
The folder structure should be similar to:
$ tree /tmp/srv/
/tmp/srv/
└── context
└── your-file.txt
Undeploy an artifact
When you create rules to deploy packages, you should also define rules to undeploy them. Forthis plugin, undeployment removes the artifact that was deployed. The rule will use the state of the deployment to determine which files must be deleted.
Define an undeploy rule
The rule definition in xl-rules.xml
is:
<rule name="example.ArtifactDeployed.DESTROY" scope="deployed">
<conditions>
<type>example.ArtifactDeployed</type>
<operation>DESTROY</operation>
</conditions>
<steps>
<os-script>
<script>scripts/undeploy-artifact</script>
</os-script>
</steps>
</rule>
Notes:
- The
operation
isDESTROY
. - Deploy automatically sets the
order
anddescription
. - The step is an
os-script
step. The script behind the step is responsible for deleting the file on the server.
Undeploy script
The FreeMarker variable for the previousDeployed
object is automatically added to the freemarker-context
. This allows the script to refer to the properties of the previous deployed object such as file name.
The Unix script undeploy-artifact.sh.ftl
contains:
echo "Undeploying file on Unix"
rm ${previousDeployed.container.home + "/context/" + previousDeployed.file.name}
echo "Done"
Test the undeploy rule
After successfully deploying the artifact, roll back the deployment or undeploy the application. If you have defined undeployment rules for all deployeds or used the sample code provided by XebiaLabs, the deployment plan looks like this:
Restart the server
Restarting the server is an advanced procedure because it requires a script rule, which is written in Jython.
You created a rule that copies an artifact to the server. To correctly install the artifact, you must stop the server at the beginning of the deployment plan and start it again in the end. This requires two more steps:
- One script that stops the server by calling the stop script
- One script that starts the server by calling the start script
A full implementation requires four scripts:
- One script that stops the server for Unix
- One script that starts the server for Unix
- One script that stops the server for Windows
- One script that starts the server for Windows
Define a restart rule
The script rule is defined in xl-rules.xml
as follows:
<rule name="example.Server.startStop" scope="plan">
<planning-script-path>planning/start-stop-server.py</planning-script-path>
</rule>
Notes:
- The
scope
isplan
because the script must inspect all deployeds of the specific sub-plan to make its decision. The rule contributes one start step and stop step per sub-plan, and rules with the plan scope are only triggered once per sub-plan. - The rule has no conditions because the script will determine if the rule will contribute steps.
- The rule refers to an external script file in a location that is relative to the plugin definition.
Restart server script
The script start-stop-server.py
contains:
from java.util import HashSet
def containers():
result = HashSet()
for _delta in deltas.deltas:
deployed = _delta.deployedOrPrevious
current_container = deployed.container
if _delta.operation != "NOOP" and current_container.type == "example.Server":
result.add(current_container)
return result
for container in containers():
context.addStep(steps.os_script(
description="Stopping server %s" % container.name,
order=20,
script="scripts/stop",
freemarker_context={'container': container},
target_host=container.host)
)
context.addStep(steps.os_script(
description="Starting server %s" % container.name,
order=80,
script="scripts/start",
freemarker_context={'container': container},
target_host=container.host))
The freemarker_context={'container': container}
is required to make the container object available in the FreeMarker context.
The rules demo plugin also includes a dummy script called start.sh.ftl
that contains:
echo "Starting server on Unix"
In a real implementation, this script must contain the commands required to start the server.
- The script starts with:
- An import statement of an utility class.
- The method definition of
containers()
. - A loop that iterates all containers and creates steps; this is the starting point of the code that will be executed.
- The
containers()
method determines which containers need to be restarted and collects them in a set. The set data structure prevents duplicate start and stop steps.- The method iterates over the
deltas
and selects the deployed withdeployedOrPrevious
, regardless if it isDESTROY
,CREATE
, and so on. - It retrieves the container of the deployed and stores it in
current_container
. - The container is added to the set of containers that must be restarted if:
- The
operation
is notNOOP
. You perform actions when the operation isCREATE
,MODIFY
, orDESTROY
. - The
type
of the container isexample.Server
. This rule will be triggered for every plan and every deployment. Ensure that the delta is related to a relevant container.
- The
- The method iterates over the
- The script iterates over all containers that must be restarted.
- The
freemarker_context
map contains a reference to the container. - In the start and stop steps, the
steps
factory is used to construct the steps by name. Notes:- The
os_script
step is used to execute the script. - The Jython naming convention (with underscores) is used to refer to the step.
- The orders for the stop (20) and start (80) steps will ensure that they are ran before and after the deployment of the application.
- Use the
addStep
method to add the constructed step directly to thecontext
.
- The
- The
- If Deploy does not find deltas for the sub-plan, the start and stop steps will not be created.
Test the server restart
To test the server restart rules, set up a deployment as described in Test the deployment rules. The deployment plan should look like:
The steps to start and stop server are added even when application is undeployed:
Roll back a deployment
The plugin that you create when following this tutorial does not require any extra updates to support rollbacks. Deploy automatically generates checkpoints for the last step of each deployed. When a user rolls back a deployment that has only been partially executed, the roll back plan will contain the steps for the opposite deltas of the deployeds for which all steps have been executed.
If you have more advanced rollback requirements, see Using checkpoints.
Next steps
After finishing this tutorial, you should have a good understanding of rules-based planning, and you should be able to find the information you need to continue creating deployment rules.
The code presented in this tutorial is available in the rules demo plugin, which you can find in the samples
directory of your Deploy installation. The demo plugin contains additional examples.
If you want to change the behavior of an existing plugin, you can disable predefined rules and redefine the behavior with new rules. For more information about this, see Disable a rule.