Getting started with DSL

10 minute readReferenceDeveloper productivity

Getting help for DSL methods

To get information on the DSL methods, use the evalDsl --describe argument as follows:

  • Obtaining the complete list of DSL methods for CloudBees CD/RO objects:

    ectool evalDsl dsl --describe 1
  • Obtaining DSL method details for a specific CloudBees CD/RO object, such as application :

    ectool evalDsl application --describe 1

    Or a microservice :

    ectool evalDsl service --describe 1
  • Obtaining details for a specific API, such as getProcedure :

    ectool evalDsl getProcedure --describe 1

Running DSL

You can evaluate a DSL script in one of these ways:

From the command-line interface

Use the evalDsl command to evaluate and run a DSL script. See the command evalDsl for a list of the supported arguments.

These ectool examples show how to evaluate a DSL script.

  • When specifying the DSL text, enter ectool evalDsl <dsl>. For example:

    ectool evalDsl "project 'My WAR file' "
  • When specifying the path to the DSL script on the client, enter evalDsl --dslFile <dslFile>. For example:

    ectool evalDsl --dslFile c:/dslScripts/myWarFile.dsl

Using the RESTful API

Go to https://<cloudbees-flow-server>/rest/doc/v1.0/, where <cloudbees-flow-server> is the host name or IP address of the CloudBees CD/RO server.

  1. Select the /server/dsl method from the server group and click Try it out.

  2. Set request to evalDsl and enter the DSL script to evaluate in the dsl field.

  3. Scroll down past all the parameters and click Execute.

The response appears in the Responses area and includes the original request along with the response body.

Request:

https://<CloudBees-flow-server>/rest/v1.0/server/dsl?request=evalDsl&dsl=project%20'ProjectTwo'

Response:

{ "project": { "projectId": "c69d55fc-f05f-11e9-b455-001c42c3ce23", "projectName": "ProjectTwo", "createTime": "2019-10-16T21:56:15.590Z", "lastModifiedBy": "admin", "modifyTime": "2019-10-16T21:56:15.590Z", "owner": "admin", "processCount": "0", "propertySheetId": "c69d7d0e-f05f-11e9-b455-001c42c3ce23", "stageCount": "0", "tracked": "1" } }

From the DSL object editor

The DSL object editor is available from within the visual editors for application, dashboard, environment, environment template, microservice, pipeline, and release objects. It provides a convenient way to view or edit the underlying DSL for the object. Buttons let you toggle between the object editors and the DSL editor.

The DSL editor simplifies the modeling process, because you can run DSL code from the CloudBees CD/RO Deploy UI rather than using an external IDE or command-line interface via the ` evalDsl` API call.

The following example shows how access the DSL editor from the pipeline editor.

  1. Navigate to the pipeline object list and select a pipeline. The selected pipeline displays in the pipeline editor.

  2. Click the DSL Editor button from the tools bar. The DSL editor opens and displays the DSL for the current pipeline object.

  3. Click the Edit button. The DSL editor opens the DSL for editing.

  4. Make a change to the DSL code and then click OK. Your changes are saved and you are returned to the pipeline editor.

Common Use Cases

Generating DSL for CloudBees CD/RO objects

To generate a DSL script for an existing CloudBees CD/RO object, which was created through a Perl API, a REST API, or the UI, enter

ectool generateDsl <path>

where <path> is the path to the CloudBees CD/RO object for which you want to generate the DSL script.

For example, if you have a resource named local in your CloudBees CD/RO instance:

  1. Run the following command to redirect the output to a file (for example, myScript.dsl):

    ectool generateDsl /resources/local > myScript.dsl

    This command generates output redirected to the specified file, which looks similar to the following:

    resource 'local', { description = 'Local resource created during installation.' artifactCacheDirectory = null hostName = '192.168.10.10' hostType = 'CONCURRENT' port = '7800' proxyCustomization = null proxyHostName = null proxyProtocol = null repositoryNames = null resourceDisabled = '0' shell = null trusted = '0' useSSL = '1' workspaceName = null zoneName = 'default' }
  2. Use the script file created in the previous steps with the evalDsl command to create or update the resource in CloudBees CD/RO .

    You can also edit the file to add or update resource attributes before using the script with evalDsl.

    ectool evalDsl --dslFile myScript.dsl

Passing parameters

Create a template script using script parameters instead of hard-coding all the values in the script. You can then invoke the same script with different parameter values each time to create different CloudBees CD/RO object instances. For example, you could use the following script to create a resource that uses SSL in the secure zone:

zone 'secure' resource() { resourceName = args.resourceName hostName = args.resourceIP hostType = 'CONCURRENT' resourceDisabled = '0' trusted = '1' useSSL = '1' zoneName = 'secure' }

The script has the values args.resourceName and args.resourceIP for the resourceName and hostName resource attributes respectively. These argument or parameter values can be passed from the command line to the DSL script in JSON form using the following command:

For Linux:

ectool evalDsl --dslFile myScript.dsl --parameters '{"resourceName":"MyFirstResource", "resourceIP":"192.168.10.12"}'

For Windows:

ectool evalDsl --dslFile myScript.dsl --parameters "{\"resourceName\":\"MyFirstResource\", \"resourceIP\":\"192.168.10.12\"}"

Note the special handling required on Windows for passing command-line arguments that contain double quotes and spaces. To allow spaces and other special characters in a command-line argument, Windows requires wrapping the value in quotes. Also, if the value itself contains quotes, you must escape those quotes by using a backslash ‘\’. For alternate method for passing parameter values for DSL, see Passing parameters using a file.

Passing parameters using a file

Parameters to a DSL script can be passed using a file that contains the parameters in JSON format as follows:

ectool evalDsl --dslFile myScript.dsl --parametersFile myParams.json

Where the file myParams.json may contain:

{ "resourceName" : "MyFirstResource", "resourceIP" : "192.168.10.12" }

Specifying contexts

CloudBees CD/RO DSL supports a simple and intuitive nested structure to represent the logical structure of CloudBees CD/RO objects. This allows the DSL methods to be evaluated in a specific context, such as with respect to a CloudBees CD/RO project, application or microservice, or pipeline.

For example, the following is a very simple script that can create an application with an application tier in a project:

// {PRODUCT} DSL engine will create project 'Default' unless it already exists project ('Default') { //'Deploy world' application will be created within 'Default' project unless it already exists application('Deploy world') { //'Web Tier' application tier will be created within 'Deploy world' application unless it already exists applicationTier('Web Tier') } }

Using CloudBees CD/RO APIs in a DSL script

All CloudBees CD/RO APIs available through ec-perl ` are available for use in your DSL script with the exception of `publishArtifactVersion and retrieveArtifactVersions. The syntax for invoking a CloudBees CD/RO API in DSL is as follows:

<methodName> (argumentName1: value1, argumentName2:value2, ... )

For example, the getProcedure API can be invoked as:

def proc = getProcedure (procedureName: 'RunInstances', projectName: 'DeployUtilities')

See CloudBees CD/RO Perl API Commands for the complete list of API commands.

Understanding transactions in DSL

By default, all operations invoked in a single DSL script run in one transaction. This means that if an error is encountered in the script, the entire script is rolled back. For example, in the following script:

// -- Transaction begins here project ('MyTestProject') application('MyTestApp') { applicationTier('Tier1') { process('Deploy') { processStep('step1', applicationTierName: 'Tier1', processStepType: 'command', errorHandling: 'failProcedure', subproject: '/plugins/EC-Core/project', subprocedure: 'RunCommand', actualParameter: [ shellToUse: 'sh', commandToRun: 'echo hi' ]) } } } // -- Transaction ends here

if an error is encountered while the script creates the process step step1 after creating the process, then the application tier, the application, then the entire transaction is rolled back to avoid a partially created application, application tier, and process. However, as with other CloudBees CD/RO APIs, if the system considers the error to be retryable (such as a database locking error), then the entire DSL is re-evaluated.

When using certain CloudBees CD/RO APIs in the DSL script, you must run them in a separate transaction. To do so, enclose the required part of the script in a transaction . For example, if you request a procedure to start in the DSL script and want to monitor its progress in the same script, you must commit the request in a separate transaction before monitoring begins. This lets the system pick up the request for execution. The following example shows how to do so:

// -- Transaction one begins here procedure projectName: ‘Hello Project’, procedureName: ‘testRunProcedure’ def resp // -- Transaction one ends here // -- Transaction two begins here transaction{ // calling the runProcedure in its own transaction // so that the procedure kicks off when this script is // running and we can monitor its progress. resp=runProcedure( projectName: 'Hello Project', procedureName: 'testRunProcedure', actualParameter: [ friend: 'James', ] ) } // -- Transaction two ends here // -- Transaction three begins here // Now let's grab the jobId launched to run the procedure def id=resp.jobId // Let's wait for it to finish def String status='' while(status != "completed") { status=getProperty(propertyName: 'status', jobId: id).value; println "status: $status" sleep (1000) } // -- Transaction three ends here
def resp // Calling the runProcedure in it's own transaction // so that the procedure kicks off when this script is // running and we can monitor its progress. transaction{ resp=runProcedure( projectName: 'Hello Project', procedureName: 'testRunProcedure', actualParameter: [ friend: 'James', ] ) } // Now let's grab the jobId launched to run the procedure def id=resp.jobId // Let's wait for it to finish def String status='' while(status != "completed") { status=getProperty(propertyName: 'status', jobId: id).value; println "status: $status" sleep (1000) }

The above script has three transactions:

  1. Before the transaction block

  2. The transaction block itself within which the procedure run request happens

  3. After the transaction block where it polls for the procedure to complete

When evaluating such a DSL script, whether the script is retried after a retryable error depends on where the error occurs. If it occurs before the transaction block is encountered, then the script is retried. But if it occurs after the transaction block is encountered, then the script is not retried.

Reusable DSL content

Reusable DSL content can be referenced with the clientFiles argument to the evalDsl API command. This means that the DSL file/code being evaluated can use methods and other DSL content defined in a local directory referenced with the clientFiles argument.

  1. Create a local file named DslBaseScript.groovy with the following content in a directory named ./sharedFiles. This file contains utility methods you wish to make available to DSL scripts evaluated locally.

    Details
    package com.electriccloud.commander.dsl.sample import org.codehaus.groovy.control.CompilerConfiguration import com.electriccloud.commander.dsl.DslDelegate import com.electriccloud.commander.dsl.DslDelegatingScript abstract class DslBaseScript extends DslDelegatingScript { /** * Utility method for creating a pipeline based on the pre-defined template */ def createPipelineFromTemplate(String projectName, String pipelineName) { evalDslScript('scripts/pipelineTemplate.dsl', [projectName: projectName, pipelineName: pipelineName]) } /** * Utility method for adding pre gate approval task to the given * pipeline stage */ def addPreGateApprovalTask(String projectName, String pipelineName, String stageName) { evalDslScript('scripts/pregateApprovalTask.dsl', [projectName: projectName, pipelineName: pipelineName, stageName: stageName]) } // boiler-plate code to evaluate dsl scripts from within dsl def evalDslScript(String dslFile, def args=[:]) { // Find file in classpath InputStream stream = this.scriptClassLoader .getResourceAsStream(dslFile) def dslScript = stream?.text CompilerConfiguration cc = new CompilerConfiguration(); cc.setScriptBaseClass(DelegatingScript.class.getName()); GroovyShell sh = new GroovyShell(this.scriptClassLoader, cc); DelegatingScript script = (DelegatingScript)sh.parse(dslScript) script.setDelegate(this.delegate); script.binding = new Binding(args: args) return script.run(); } /** * Intercept the DslDelegate so it can be set as the delegate on the * dsl scripts being evaluated in context of the parent dsl script. * Before setting the delegate, also capture the script's class loader * before the dslDelegate hijacks the calls. This is needed to get the * reference to the groovy class loader used for evaluating the DSL * script passed in to <code>evalDslScript</code>. */ private def delegate; private def scriptClassLoader; void setDelegate(DslDelegate delegate) { this.scriptClassLoader = this.class.classLoader this.delegate = delegate; super.setDelegate(delegate) } // end boiler-plate
  2. Now, create a DSL script that references the utility methods in DslBaseScript.groovy. For example:

    import groovy.transform.BaseScript import com.electriccloud.commander.dsl.sample.DslBaseScript // DslBaseScript encapsulates the magic for invoking dsl scripts @BaseScript DslBaseScript baseScript // Examples for using shared scripts and code // 1. Evaluate basic dsl script evalDslScript 'scripts/project.dsl' // 2. Create a pipeline based on a template createPipelineFromTemplate 'Pipeline project', 'Sample pipeline' // 3. Add a task based on a template to existing pipeline addPreGateApprovalTask 'Pipeline project', 'Sample pipeline', 'Stage 1'
  3. Using the ectool command, evaluate the DSL script:

    ectool evalDsl --dslFile referenceScript.groovy --clientFiles ./sharedFiles

    Any Groovy files contained in the directory specified for clientFiles are available to the script evaluated by evalDsl.

Accessing external libraries

You can use external .jar files with DSL. CloudBees CD/RO DSL is based on Groovy so that you can take advantage of all Groovy and Java capabilities. You can use any Groovy or Java libraries in your DSL script. The libraries can be made available to the CloudBees CD/RO DSL runtime engine when the DSL script is executed using the serverLibraryPath parameter.

  1. Create a file named httputil.groovy with the following content. The script uses the groovyx.net.http.HTTPBuilder class to make an HTTP GET request.

    import groovyx.net.http.HTTPBuilder import static groovyx.net.http.Method.GET import static groovyx.net.http.ContentType.JSON String url = “https://www.example.com” String uriPath = “/sample-rest-call HTTPBuilder http = new HTTPBuilder(url) http.request(POST, JSON) { uri.path = uriPath send URLENC, content response.success = { resp, reader ->; String jsonResponse = (reader.readLines().join() as String) println new JsonSlurper().parseText(jsonResponse) } response.failure = { resp, reader ->; log.error "Request failed : [URI : ${uriPath}, Status: ${resp.status}]" } response.’401’= {resp ->; println “Status 401 received” }
  2. Create a directory named /opt/dslSamples/lib and make it accessible from the CloudBees CD/RO server with the following .jar files:

    http-builder-0.6 json-lib-2.3-jdk15.jar xml-resolver-1.2.jar
  3. Evaluate script using the following command:

    ectool evalDsl --dslFile httputil.groovy --serverLibraryPath /opt/dslSamples/lib

    When the DSL is evaluated on the CloudBees CD/RO server, any .jar files contained in the directory specified for serverLibraryPath will be available to the CloudBees CD/RO DSL runtime engine.

Any Groovy files and Java class files contained in the directory specified for serverLibraryPath will also be available to the script evaluated by evalDsl. For example, if the directory contains a Groovy class my.sample.dsl.DslUtil in the directory structure my/sample/dsl/DslUtil.groovy, the script can use the Groovy class by importing the class in the script as with any other class.

Debugging DSL scripts

You can use debug logging as well as the println method for debugging DSL scripts.

Using debug logging

You can use the debug argument to return debug messages in the evalDsl response for your script while it is evaluated by the CloudBees CD/RO DSL engine. These messages are useful for debugging DSL scripts. For example:

ectool evalDsl --dslFile myScript.dsl --debug 1

or

ectool evalDsl "project 'My WAR file'" --debug 1

Using the println method

You can use the standard Groovy println method in your DSL script to print messages to the client console while it is evaluated by the CloudBees CD/RO DSL engine. The line numbers for lines that produced println output are included. For example, entering ectool evalDsl --dslFile myfile.dsl, where myfile.dsl contains:

def result for (int i =0; i< 10; i++) { println 'Creating project: ' + 'ABTest-' + i result = project 'ABTest-' + i, resourceName: 'res1' } result

returns the following output:

<response requestId="1" nodeId="10.0.1.15"> <value> Result: project[name=ABTest-9,id=7777e4ff-7941-11e6-94c1-34e6d71279c8] Console output: Line 0003: [OUT] Creating project: ABTest-0 Line 0003: [OUT] Creating project: ABTest-1 Line 0003: [OUT] Creating project: ABTest-2 Line 0003: [OUT] Creating project: ABTest-3 Line 0003: [OUT] Creating project: ABTest-4 Line 0003: [OUT] Creating project: ABTest-5 Line 0003: [OUT] Creating project: ABTest-6 Line 0003: [OUT] Creating project: ABTest-7 Line 0003: [OUT] Creating project: ABTest-8 Line 0003: [OUT] Creating project: ABTest-9 </value> </response>

If you also use the debug argument , the println output is interleaved with the debug logging to the console. The line numbers for lines that produced println output and debug logging are included. The println output is similarly interleaved with debug logging for exceptions where the debug logs are returned in the evalDsl response. For example, entering ectool evalDsl --dslFile myfile_that_throws_exception.dsl --debug 1, where myfile_that_throws_exception.dsl contains:

project 'printingInInvalidDsl' println("Causing NPE now..") def a a.name //will cause NPE println("Should not print")

returns the following output:

ectool error [InvalidScript]: Unknown exception during DSL eval at line 4: Cannot get property 'name' on null object Line 0001: DSL method 'project' Line 0001: Checking if project exists with args {projectName=printingInInvalidDsl} Line 0001: project exists: false Line 0001: Invoking 'createProject' with args {projectName=printingInInvalidDsl} Line 0001: Done invoking 'createProject' Line 0002: [OUT] Causing NPE now.. Details: Cannot get property 'name' on null object