Intro
All actions in deploying an application can be broken down into two categories: moving data (files) and running commands.
In a typical deployment, Digital.ai Deploy performs these two actions on a remote system, to which a connection is made via SSH for Unix/Linux operating systems and WinRM for Windows.
Deploy has a convenient control task called Check Connection that proves that these two actions can be performed successfully. It transfers a dummy data file and runs a command to list the contents of a temporary directory. It proves that the protocols are correctly configured, firewalls and ports are open, and the login credentials are valid.
So are we ready to deploy?
Some users of Deploy are tempted at this point to structure a deployment using plugins that provide these exact two actions in their most basic form: the command plugin with its cmd.Command object type, and the file plugin with its file.File object type.
A user goes into Deploy and configures a deployment in this fashion:
- Commands that precede the file transfers
- cmd.Command object with order = 45
- cmd.Command object with order = 55
- File transfers
- file.File object for the first file with default order 60
- file.File object for the second file with default order 60
- file.File object for the third file with default order 60
- file.File object for the fourth file with default order 60
- Commands that follow the file transfers
- cmd.Command object with order = 65
- cmd.Command object with order = 75
What are the pros and cons of this approach?
- Easy to configure
- Easy read and comprehend
On the other hand, this approach has some shortcomings:
- Fragile - you're working with an open command line susceptible to errors
- Incomplete - the commands don't take rollbacks and reruns into account effectively
- Not portable - you'll have to rewrite the commands for another OS such as Windows
- Doesn't identify that all these items go together if they are part of a package containing other deployables
- Doesn't provide the benefits of Deploy's object model - for example, if you wanted to "subclass" this configuration for behavior slightly different than this, you have to rewrite it.
Best Practices
XebiaLabs recommends these Best Practices for Deploy when it comes to deployments not already supported by an existing plugin.
- Combine the file artifacts, both text and binary, into a single zip-style archive
This follows the same rationale for bundling application files together as a jar, war, or ear file. They move together through your CI/CD pipelines as a single unit, developed and deployed together. Pack them in the build job, and unpack them when they reach their final home on the target system.
- Make use of classpath resources when able
These might be installation binaries or control templates that don't change between deployed versions and therefore don't have to be bundled into the actual application files.
- Control the commands required for your deployment with xl-rules and classpath scripts
The scripts are easily parameterized and Deploy can send the Linux or Windows version of a script depending on the OS targeted.
A Plugin Example
Now we'll begin constructing a plugin by working in the XL-DEPLOY-SERVER/exrt directory. As a first step, add a definition to you synthetic.xml:
<?xml version="1.0" ?>
<synthetic xmlns="http://www.xebialabs.com/deployit/synthetic">
<type type="demo.DeployedBestPractices"
extends="udm.BaseDeployedArtifact"
deployable-type="demo.BestPractices"
container-type="overthere.Host">
<generate-deployable type="demo.BestPractices" extends="udm.BaseDeployableArtifact" />
<property name="targetdir1" />
<property name="targetdir2" />
<property name="targetdir3" />
<property name="targetdir4" />
</type>
</synthetic>
This gives us the ability to define a custom artifact along with some property for the deployment, in this case, the target directory for each of the files when we unzip them.
Now on to defining the deployment behavior via xl-rules.
<?xml version="1.0"?>
<rules xmlns="http://www.xebialabs.com/xl-deploy/xl-rules">
<!-- Add your <rule ...> and <disable-rule ...> elements here -->
<rule name="demo.DeployedBestPractices.Create" scope="deployed">
<conditions>
<type>demo.DeployedBestPractices</type>
<operation>CREATE</operation>
</conditions>
<steps>
<os-script>
<description>Execute script 1</description>
<script>demo/createBestPractices1</script>
<order>45</order>
<upload-artifacts>false</upload-artifacts>
</os-script>
<os-script>
<description>Execute script 2</description>
<script>demo/createBestPractices2</script>
<order>55</order>
<upload-artifacts>false</upload-artifacts>
</os-script>
<os-script>
<description>Handle the files</description>
<script>demo/createBestPracticesUpload</script>
<order>60</order>
</os-script>
<os-script>
<description>Execute script 3</description>
<script>demo/createBestPractices3</script>
<order>65</order>
<upload-artifacts>false</upload-artifacts>
</os-script>
<os-script>
<description>Execute script 4</description>
<script>demo/createBestPractices4</script>
<order>75</order>
<upload-artifacts>false</upload-artifacts>
</os-script>
</steps>
</rule>
</rules>
We have replaced our four commands with an os-script section, pointing to a script in the Deploy classpath. The ext directory is now structured like this:
$ tree
├── demo
│├── createBestPractices1.sh.ftl
│├── createBestPractices2.sh.ftl
│├── createBestPractices3.sh.ftl
│├── createBestPractices4.sh.ftl
│ └── createBestPracticesUpload.sh.ftl
├── readme.txt
├── synthetic.xml
└── xl-rules.xml
1 directory, 8 files
The four "command" scripts now look like this:
$ cat demo/createBestPractices1.sh.ftl
echo "Executing command 1"
$ cat demo/createBestPractices2.sh.ftl
echo "Executing command 2"
$ cat demo/createBestPractices3.sh.ftl
echo "Executing command 3"
$ cat demo/createBestPractices4.sh.ftl
echo "Executing command 4"
And we have added an upload step to handle the files within our zip archive and move them to their proper directories, taking advantage of Freemarker templating for properties of the deployed object:
$ cat demo/createBestPracticesUpload.sh.ftl
unzip -o ${deployed.file.path} myfile1 -d ${deployed.targetdir1}
unzip -o ${deployed.file.path} myfile2 -d ${deployed.targetdir2}
unzip -o ${deployed.file.path} myfile3 -d ${deployed.targetdir3}
unzip -o ${deployed.file.path} myfile4 -d ${deployed.targetdir4}
Here is our deployment output for the script, which illustrates how Deploy uploads the script and the artifacts are uploaded to a temporary directory, and then executes the script from there on the host's operating system.
Notice that the rules file just specified demo/createBestPracticeUpload, without the .sh or .ftl extensions. Because Deploy knows this deployment is going to a Linus system, it will look for the version of the script that has the .sh extension. The .ftl extension directs Deploy to process the script through Freemarker. If we wanted to deploy to Windows, we would have included a .bat or .cmd version of the script.
We end up with the same result as we had with the four command and the four file objects. And there are many more options available in Deploy to make this example fully functional:
This type can be subclassed for variations on the core behavior.
Rollback behavior can be controlled with additional rules. See the rules reference for all the options available with Rules.
Parameterization is very flexible with Freemarker. See https://freemarker.apache.org/docs/index.html.
Comments
Please sign in to leave a comment.