Jenkins Integration Example

The Pseudo Code example is used as the basis for the following use case where a Jenkins CI server job, which builds a FHIR server, defines a post build task that calls the Touchstone API RESTful web services. The Jenkins Groovy plugin is used where we create an externally called Groovy script that executes the authenticate service, test execution service and iterative status check service calls.

  • The installation and configuration of a Jenkins CI server, the corresponding FHIR server build job definition within the Jenkins environment, and installation of the Jenkins Groovy plugin is described in the Jenkins documentation and left as an exercise to the user.
  • This use case was successfully implemented within a Jenkins 2.x CI server installed on a CentOS Enterprise Linux server.

Groovy Script Definition

The Groovy script uses the HTTPBuilder class library and corresponding supporting libraries in order to provide the means to call the Touchstone API HTTP / RESTful web service endpoints.

Imports
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.XML
import static groovyx.net.http.Method.GET
import static groovyx.net.http.Method.POST

Authentication

The body attribute of the request will contain the Touchstone user account email address and password. These values are to be replaced with the actual values for the given user.

The println statements provide execution output to the Jenkins job console log.

The apiKey variable is set for subsequent use in the next web service calls.

If the assert of the apiKey string value evaluates to false, the script will fail and therefore cause the Jenkins job to fail.

// Authenticate with the Touchstone Server and get an API Authorization Token
String apiKey
def httpAuth = new HTTPBuilder('http://touchstone.aegis.net/touchstone/api/authenticate')

httpAuth.request(POST, XML) {
    headers.'Accept' = 'application/xml;charset=UTF-8'
    headers.'Content-Type' = 'application/xml;charset=UTF-8'
    body = '<?xml version="1.0" encoding="UTF-8"?><authenticateRequest xmlns="http://touchstone.aegis.net/api"><email>test.user@example.com
    </email><password>password</password></authenticateRequest>'

    response.success = { resp, authenticateResponse ->
        println "POST Response Status : ${resp.statusLine}"
        println "Authenticate Response info : ${authenticateResponse.info}"
        apiKey = "${authenticateResponse.'API-Key'}"
        println "Authenticate Response API-Key : ${apiKey}"
    }

    response.failure = { resp, authenticateResponse ->
        println "Failure Status : ${resp.statusLine}"
        println "Authenticate Response error : ${authenticateResponse.error}"
    }
}

assert apiKey != null : 'Authentication failed! No API-Key returned.'

Execute Test

Note that the apiKey value is set for the custom HTTP header ‘API-Key’.

The body attribute of the request will contain the previously defined Touchstone Test Setup name that belongs to the authenticated user. This value is to be replaced with the actual Test Setup name for the given user.

The println statements provide execution output to the Jenkins job console log.

The execId variable is set for subsequent use in the next web service calls.

If the assert of the execId string value evaluates to false, the script will fail and therefore cause the Jenkins job to fail.

// Execute a Test Setup
String execId
def httpExecute = new HTTPBuilder('http://touchstone.aegis.net/touchstone/api/testExecution')

httpExecute.request(POST, XML) {
    headers.'API-Key' = apiKey
    headers.'Accept' = 'application/xml;charset=UTF-8'
    headers.'Content-Type' = 'application/xml;charset=UTF-8'
    body = '<?xml version="1.0" encoding="UTF-8"?><executeTestRequest xmlns="http://touchstone.aegis.net/api"><testSetup>
    FHIR3-2-0-Connectathon17-Patient-02-Formal-FHIRServer-03-PatientRead--All</testSetup></executeTestRequest>'

    response.success = { resp, executeTestResponse ->
        println "POST Response Status : ${resp.statusLine}"
        println "Test Execute Response info : ${executeTestResponse.info}"
        println "Test Execute Response exec url : ${executeTestResponse.testExecURL}"
        execId = "${executeTestResponse.testExecId}"
        println "Test Execute Response exec id : ${execId}"
    }

    response.failure = { resp, executeTestResponse ->
        println "Failure Status : ${resp.statusLine}"
        println "Test Execute Response error : ${executeTestResponse.error}"
    }
}

assert execId != null : 'Test Execution not started! No test execution id returned.'

Wait for Test Execution Completion

Note that the execId value is appended to the testExecution web service call path where the httpExecStatus variable is declared.

There is no body attribute for this GET request.

The println statements provide execution output to the Jenkins job console log.

The isDone, isPassed and execStatus variables are set and used to determine when to exit the wait while loop.

If none of the loop exit criteria is met, the sleep(15000) call will pause the loop for 15 seconds in order to provide sufficient time to pass and allow more of the test execution to complete. Although this time can be set to a different amount, the recommended minimum time to pause is 5 seconds (or 5000 milliseconds).

If the assert of the execStatus string value evaluates to false, the script will fail and therefore cause the Jenkins job to fail.

// Wait for test execution completion
// IMPORTANT - The Touchstone API requires a minimum wait time of 4 seconds between GET calls to '/api/testExecution'
boolean isDone = false
boolean isPassed = false
String execStatus
def httpExecStatus = new HTTPBuilder('http://touchstone.aegis.net/touchstone/api/testExecution/' + execId)

while (!isDone) {

    httpExecStatus.request(GET, XML) {
        headers.'API-Key' = apiKey
        headers.'Accept' = 'application/xml;charset=UTF-8'

        response.success = { resp, testExecStatusResponse ->
            println "GET Response Status : ${resp.statusLine}"
            println "Execute Status Response info : ${testExecStatusResponse.info}"
            execStatus = "${testExecStatusResponse.status}"
            println "Execute Status Response status : ${execStatus}"
            println "Execute Status Response period : ${testExecStatusResponse.startTime} - ${testExecStatusResponse.endTime}"
        }

        response.failure = { resp, executeTestResponse ->
            println "Failure Status : ${resp.statusLine}"
            println "Test Execute Response error : ${executeTestResponse.error}"
            isDone = true
        }
    }

    // Test for all valid execution completion Touchstone statuses
    if (execStatus != null && (execStatus.equals("Passed") || execStatus.equals("PassedWarning") || execStatus.equals("Failed")
         || execStatus.equals("Warning") || execStatus.equals("Skipped") || execStatus.equals("Stopped")
         || execStatus.equals("Completed"))) {
        isDone = true
        // Test for valid execution passing Touchstone statuses
        if (execStatus.equals("Passed") || execStatus.equals("PassedWarning")) {
            isPassed = true
        }
    }
    else if (isDone) {
        // Catch isDone true before waiting 15 seconds
    }
    else {
        // Wait 15 seconds before checking completion again
        sleep(15000)
    }
}

assert isPassed : 'Test Execution did not pass! Please examine the test results via the Touchstone web interface.'

Jenkins Job Configuration

The Jenkins job will be configured to execute the Groovy script in a post steps task. The following sections describe the required entries.

Post Steps Initial Configuration

Select the Jenkins job to configure and scroll down toward the bottom of the configuration page to the Post Steps section.

Select the option Run only if build succeeds to insure the Touchstone API calls are only triggered after a successful build.

Then select the Add post-build step drop down list.

../_images/jenkins_job_integration.png

Execute system Groovy script

Select Execute system Groovy script from the drop down list:

../_images/jenkins_execute_testscript.png

Groovy script file

Select the Groovy script file option and enter the fully qualified path to the touchstone.groovy script file. Replace the $SCRIPTS_HOME variable with the fully qualified directory path. Then select the Advanced link.

../_images/jenkins_groovy_script_file.png

Advanced - Expand Classpath

After the Advanced section is displayed, expand the Classpath text entry field by selecting the down arrow link to the right.

../_images/jenkins_expand_classpath.png

Advanced - Enter Classpath

The Groovy script is executed using an external Groovy SDK and therefore requires all Groovy and supporting libraries be listed in the Classpath field.

../_images/jenkins_classpath.png

The following is a complete listing of the Groovy library jars and additional supporting library jars that are required in order to successfully execute the touchstone.groovy script. Replace the $APPS_HOME variable with the fully qualified directory path to the Groovy SDK library files. The highlighted files are supporting jars that were separately downloaded and added to the directory.

  • $APPS_HOME/groovy-2.4.4/lib/ant-1.9.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/ant-antlr-1.9.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/ant-junit-1.9.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/ant-launcher-1.9.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/bsf-2.4.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/commons-cli-1.2.jar
  • $APPS_HOME/groovy-2.4.4/lib/commons-collections-3.2.1.jar
  • $APPS_HOME/groovy-2.4.4/lib/commons-logging-1.2.jar
  • $APPS_HOME/groovy-2.4.4/lib/gpars-1.2.1.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-ant-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-bsf-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-console-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-docgenerator-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-groovydoc-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-groovysh-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-jmx-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-json-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-jsr223-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-nio-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-servlet-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-sql-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-swing-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-templates-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-test-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-testng-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/groovy-xml-2.4.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/hamcrest-core-1.3.jar
  • $APPS_HOME/groovy-2.4.4/lib/http-builder-0.7.1.jar
  • $APPS_HOME/groovy-2.4.4/lib/httpclient-4.3.6.jar
  • $APPS_HOME/groovy-2.4.4/lib/httpcore-4.3.3.jar
  • $APPS_HOME/groovy-2.4.4/lib/ivy-2.4.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/jansi-1.11.jar
  • $APPS_HOME/groovy-2.4.4/lib/jcommander-1.47.jar
  • $APPS_HOME/groovy-2.4.4/lib/jline-2.12.jar
  • $APPS_HOME/groovy-2.4.4/lib/json-lib-2.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/jsp-api-2.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/jsr166y-1.7.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/junit-4.12.jar
  • $APPS_HOME/groovy-2.4.4/lib/multiverse-core-0.7.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/openbeans-1.0.jar
  • $APPS_HOME/groovy-2.4.4/lib/qdox-1.12.1.jar
  • $APPS_HOME/groovy-2.4.4/lib/servlet-api-2.4.jar
  • $APPS_HOME/groovy-2.4.4/lib/testng-6.8.13.jar
  • $APPS_HOME/groovy-2.4.4/lib/xmlpull-1.1.3.1.jar
  • $APPS_HOME/groovy-2.4.4/lib/xml-resolver-1.2.jar
  • $APPS_HOME/groovy-2.4.4/lib/xstream-1.4.7.jar