© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2023
G. PuccianiBoozang from the Trencheshttps://doi.org/10.1007/978-1-4842-9010-1_15

15. Jenkins and the Boozang Runner

Gianni Pucciani1  
(1)
Meyrin, Geneve, Switzerland
 

Fly me to the moon.

A cartoon of a robot flying to the moon on a rocket.

This chapter shows how to use Jenkins for scheduling and reporting automated tests results in a CI/CD pipeline. We will see how Boozang can be used with other tools to provide a fully automated pipeline and also how to reduce the execution time through parallel runs.

Continuous Delivery and the Need for Automating Acceptance Tests

About continuous integration and continuous delivery, a lot has been already said and written. My favorite resource is the book by Jez Humble and David Farley Continuous Delivery.1

The practice of continuous delivery is all about shortening the feedback loop by automating all the necessary steps to provide quality software to end users. And among these steps, there are of course the tests.

Continuous integration on the other hand is about frequently integrating the developments into a main branch, but not necessarily deploying this into production.

The main pillars of continuous delivery are
  • Configuration management: Which includes code versioning and dependencies management

  • Continuous integration: Which includes development practices like Extreme Programming and Test Driven Development

  • Test Strategy and Test Automation

  • Deployment automation

In these four domains, several constraints and maturity levels can be present in an organization.

In our project, a COTS integration type of project, some steps were possible and others not, either due to technological or organization constraints or by choice.

For example, until the 1.0 release, it was not possible to deliver frequently some functionalities or part of it. This is mainly to avoid having the business work with two systems.

The development work was done by the editor; therefore the continuous integration and code versioning was not under our control.

In terms of testing, unit testing was not under our control either, and our focus was on Acceptance Testing and automation of the regression tests.

The deployment automation was possible: the Jenkins pipeline was provided by the editor and adapted to our environments.

Acceptance testing can be defined as the last level of testing in most cases. Automating acceptance tests is important to catch issues that other test levels don’t catch.2 Once automated, acceptance tests can be run often and early on, unlike manual tests.

Regression testing must be part of the acceptance test suite. The challenge to cover new features can be addressed by adopting a BDD approach, as explained in Chapter 13, “Gherkin and Behavior Driven Development.”

When designing and implementing automated acceptance tests, the INVEST principles normally used for user stories are always handy to keep in mind:
  • I, Independent: Each scenario should run independently of the others and without any sequential order. This will allow you to fully benefit from the parallel execution (more on this later in this chapter).

  • N, Negotiable: They must be the result of a discussion and collaboration effort.

  • V, Valuable: They must provide a real value to the users, that is, they must prove that the released software brings the expected benefits.

  • E, Estimable: They must be clear enough to be able to estimate and plan, like User Stories.

  • S, Small: Not always for E2E case, but in general they should focus on a specific goal, not many at the same time.

  • T, Testable: Speaks for himself. ;)

In the next section we dive into the steps necessary to automate the regression tests.

Puppeteer and the Boozang Runner

Puppeteer 3 is a node.js library used for Web UI automation. Much like Selenium , it provides an API to control the browser, but compared to Selenium has some significant differences. First of all the age; the project was developed in 2017 (Selenium in 2004) by Google, hence focused on Chrome support (Selenium instead supports all the most common browsers). In terms of supported languages, Puppeteer is full JavaScript, while Selenium supports many different languages.

Compared to Selenium, Puppeteer is simpler to install and use. The protocol used is also different (DevTools4 vs WebDriver5), providing important advantages in terms of testing capabilities.

From a goal point of view, Puppeteer’s focus is not on testing, but on pure Web UI automation. It therefore lacks all those extra features that are useful for testing, like recording, but this is what Boozang does.

The Boozang runner is a node-js package6 built on top of Puppeteer that allows you to execute Boozang tests from the command line.

Another (beta) version of the Boozang runner7 is based on Microsoft Playwright.8 Both versions of the runner are freely available on GitHub under the MIT license.

When you run tests through Jenkins however, you will not use the CLI runner, but rather a docker container that encapsulates the CLI and makes it easy to run tests. This is the topic of the next section.

How to Use the Docker Runner

Let’s see how to run a simple test via the Docker runner.

First of all, make sure to have Docker installed on the machine where you will run the tests.

To execute a test, you need a “tokenized” URL, that is, the URL of the Test with your personal token (linked to your Boozang account). To get this URL, from any Test in the IDE simply open the Advanced Operations menu, select the link “Generate Tokenized URL ,” and input your Boozang account password. You will then be able to copy the URL.

A screenshot of an advanced operations webpage on Boozang I D E. An arrow points to a link on the page for generating tokenized U R L.

Generate tokenized URL in the Advanced Operations menu

A screenshot of a password entry window. It contains a test case link and a password entry field for the token execution link.

Enter password screen

A screenshot of a window with a tokenized U R L. The page contains a test case link and a token execute link that is selected.

Tokenized URL to copy

The URL format is the following:

https://eu.boozang.com/extension?token=<your personal token>&env=<environment ID>#<project ID>/<branch>/<module ID>/<test ID>/run

Once you have the URL, you need to run the Docker container with a command like:
docker run --rm -v "$(pwd):/var/boozang/" styrman/boozang-runner "<tokenized URL>"
  • The -rm options are used to remove the container after run.

  • The -v is used to specify the volume mapping9 and be able to locally store the test report that will be generated.

To see all the available options of the Docker runner, you can head to the github repository page.10

In the previous example we used a single Test, but of course this process can be followed to execute a Test Suite that contains Tests or Scenarios. It won’t be available however on Modules and Features since these two objects are mainly to group Tests and Scenarios.

Keeping Execution Time Low with Parallel Runs

Execution time of your test suite is crucial. You want your full suite, which might include hundreds or even thousands of scenarios to run easily overnight for regression testing. And you want a subset of them, critical, to run in 15 or 30 minutes so that, while implementing new tests, you can check their impact on existing tests.

Executing tests or scenarios in parallel is the main solution, combined with other good practices like using APIs to setup the initial state, and keeping the scenarios focused on a single objective.

Parallel Execution and Workers Setup

When your acceptance test suite starts growing, the execution time might soon become an issue. In this case, executing tests in parallel is your main ally.

In the NIS project we started with parallel runs early on, around the third implementation Sprint.

We initially used GNU Parallel , then used plain Jenkins pipeline, and finally moved to the new feature developed by the Boozang team, the distributed master-slave setup. In the next sections, we will go through these three options. Clearly, the latest option is the best one, and we will spend more time on it.

Boozang Workers Concepts

The workers approach is similar to an agile team. Each worker is a team member, and each team is a group. Each worker can have a scope that is like a specialization (like BA, Developer, Tester). There is finally a master that is like a PO or the backlog itself.

The master can dispatch tests to workers, based on their scope. Normally master and workers belong to the same group.

An illustration of the Boozang work concept. It presents the components of the agile team and the Boozang workers in a similar format.

Boozang distributed execution approach

Each worker must have a specific ID, and a worker can be associated with only one group.

This organization offers a lot of flexibility for running your tests in a distributed way. We will focus mainly on parallel runs, which is just one of the possible use cases for distributed runs.

In this case all the workers have the same scope, no specialization. The master dispatches scenarios to the workers as soon as a worker is available.

But it gets even better; the parallelism is at scenario iteration level: this means that when you have Examples with Scenario Outlines, examples can be run in parallel. As far as I know, Boozang is the only Test Automation tool that offers this level of parallelism.

One interesting use case for having several groups is to reserve a group for nightly run, and another for debugging. Another one is in the case of a large organization with several automation teams; each team could have its own group of workers that can run independently of others.

Boozang Workers Setup in the Jenkins Pipeline

When using a declarative pipeline in Jenkins , there are some key steps to follow to run tests in parallel.
  1. 1.

    Launch n workers (the number will be limited by the resources available on your server11). Workers will stay idle until they receive some execution tasks.

     
  2. 2.

    Launch the master with the Test Suite you want to run. The master will dispatch the execution tasks to workers as they are available.

     
Launching the Workers

In order to launch the worker containers, we have two steps: creation of the worker URL and execution of the docker run command. To define the worker URL, let’s see a snapshot taken from our pipeline with variables defined for each URL element.12

worker_url = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$worker_key&self=${self}#${params.BZPROJECT}/${params.BRANCH}"

This URL is like the previous tokenized URL already seen, with the following differences:
  • There is no Test or Suite ID at the end, which means the docker runner will run and listen for tasks from the master.

  • There are three additional fields:
    • Group: An identifier to group workers (we currently use the Jenkins build number for this). This can be the counter of the loop mentioned below.

    • Key: An identifier unique for each worker.

    • Self: It can be either 1 or 0. 1 means that the worker is private to the user linked with the token specified in the URL. 0 means that the worker can be shared among different users.

Create a loop to launch the container in detached mode to allow the pipeline to continue.
sh "nohup docker run --name bg-worker$counter -e TZ=Europe/Paris --rm -v "$/var/boozang:/var/boozang/" -v "/var/common:/var/common/" styrman/boozang-runner "$worker_url" >  ${reportsDir}/boozang-worker_${worker_key}.log &"
Let’s see the elements of this command:
  • nohup: Used to detach the command from the user session.

  • name: To name each worker container.

  • -e: Set an environment variable for the timezone, so that timestamps in the logs are aligned with your time.

  • rm: Already mentioned, it will remove the containers after run.

  • -v: Set some volumes for logs and reports.

  • The output of the command is redirected to a worker log file, that can be useful when debugging.

  • Finally the whole command is run in detached mode.

Launching the Master
To launch the master we follow two similar steps: URL creation and docker run command.
master_URL = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$master_key&self=${self}#${params.BZPROJECT}/${params.BRANCH}/${params.TEST_ID}/run"
This is a very similar URL to the one used for the workers, the only differences are
  • The key is the one of the master, another unique ID.

  • We have the information of the suite to run (TEST_ID parameter) with the “/run” to launch the execution.

sh "docker run --name bg-master -e TZ=Europe/Paris --rm -v "/var/boozang:/var/boozang/" -v "/var/common:/var/common/" styrman/boozang-runner "$master_URL" >  ${reportsDir}/boozang-master.log"

This is a very similar command to the previous one used to run the worker containers. The only difference is that now we do not use the `nohup` nor the `&` so that the pipeline will wait for this command to finish before passing to the following stages.

Reports and Log Files

When the pipeline is executed, you want to make sure to have all the necessary log and report files generated by Boozang. What are those files located and what do they contain?

Execution Reports

Test execution reports are stored, within the container, in /var/boozang. In this folder you will find the Cucumber JSON reports. These reports can be used in two ways:
  • With the Jenkins cucumber reports plugin13

  • By sending these reports to XRay (as mentioned in the previous chapter, section “Pushing Test Results Back to XRay”)

It must be noted that if you do not use the Gherkin layer, you still have access to the Boozang standard reports, but these are by default stored on the server side. If you want to download them, you need to specify the “auto-download” option in the Boozang settings.

Log Files and the Log Formatter

Each Boozang container will produce some execution logs, which will be stored as well in /var/boozang.

When running several workers, each worker will produce a log file.

As we will see later, in our Jenkins pipeline, we have a stage to aggregate the worker log files into a single one.

A screenshot of a Jenkins webpage with multiple lines of code for log execution. The page highlights the worker logs and the aggregated log.

Log files published on Jenkins

Having an aggregated log file allows us to use the log formatter that is available from the add-on icon:

A screenshot of a log formatter tab. It has tick boxes for auto format, retrieving test worker logs, and fields for performance thresholds.

Log formatter

Using the log formatter will give you the possibility to better troubleshoot the scenarios, adding a structure, colors, and useful performance information directly from your Jenkins page:

A screenshot of a Jenkins window with the formatted logs. Create and cancel policy under policy cancellation in the completed list is selected.

Log files formatted

Root Cause Analysis Report

The Root Cause Analysis report (see Chapter 11, “Reporting and Troubleshooting”) is a special type of report that is produced by a script that you can download from GitHub at the URL:

https://raw.githubusercontent.com/ljunggren/bz-utils/main/scripts/generate_summary.sh ".

Here is the pipeline stage used to produce the RCA report :

Twenty-three lines of code. The code performs a root cause analysis and publishes the result.

RCA report generation stage

The main steps are
  • Install prerequisites packages: jq and bc

  • Download the shell script, adapting the rights if needed

  • Run the script giving as input the JSON reports files

  • Copy the html reports produced in a report folder of later use and display

Publishing Reports on Jenkins and JIRA

To publish reports on Jenkins , we rely on the Cucumber reports plugin14 and on the HTML Publisher plugin.15

We have four main sections to publish for each Jenkins build:
  • Cucumber reports → Cucumber report plugin

  • Log files → HTML publisher

  • Cucumber reports → HTML publisher

  • RCA report → HTML publisher

A screenshot of cucumber reports. It lists worker logs, cucumber J son files, and R C A report.

Reports sections

Here are the two stages used to publish these reports.

Cucumber Reports

The following picture shows the pipeline stage for producing the Cucumber reports .

Seventeen lines of code. The code publishes the Cucumber report after collecting the results with the cucumber report publisher plugin.

Stage to publish cucumber reports

Boozang when running Gherkin scenarios will produce a JSON result file for each Scenario, and in case of Scenario Outline, for each iteration. You will then have a lot of JSON files in the report folder.

To send the execution reports to XRay, all those files need to be aggregated into a single one (more on this in the next section), while for the Cucumber report plugin we let the plugin take care of it (mergeFeatureById set to true).

The main thing to note in this stage is that we give all the reports to use (*.json) via the fileIncludePattern property, and these files will be looked for in the jsonReportDirectory.

We use the fileExcludePattern to avoid picking up a JSON file that is the aggregation of all the JSON files.

The results will then be shown as in the following picture.

A screenshot of the cucumber report with a pie chart. The pie chart represents the passing and failing statistics for the features from the report.

Cucumber reports

These reports are interesting and quite nice for communicating the results. For troubleshooting they allow you to zoom in the failures and see the details.

HTML Reports

The following step for the HTML reports is shown. We execute this step in the post section, so that it will be produced even if some of the previous stage fails.

Multiple lines of code in 3 sections. The code publishes worker logs, J son reports, and a root cause report.

Each section creates a menu in the left bar of Jenkins with the name mentioned in the “reportName” property. The options are quite self explanatory; you need to specify the files you want to include in the page and the folder containing those files, always relative to the Jenkins workspace.

A screenshot of the dashboard on the Jenkins window. The worker logs, cucumber J son files, and R C A report on the left panel are highlighted.

HTML publisher menus

JIRA Reports

The stage to import test results in JIRA has been already covered in Chapter 14, “JIRA with Boozang via XRay.”

For simplicity, we report it again here as follows.

Twenty-five lines of code. The code uploads cucumber results to Jira.

Complete Working Pipeline

In this section we review our pipeline which has now reached a good level of maturity and stability.

The following picture shows the main stages, and then the following sections will review the code in each stage.

The exact code was reported to help the reader and see a real working example.16

A flowchart of a working pipeline in Jenkins. The flowchart has 3 stages namely initial settings, pipeline stages, and post.

Jenkins pipeline

The first step to prepare for a parallel execution with different credentials is to set up the test users. This is done directly in the application and then aligned in Boozang as explained in the next section.

Test Users Setup

A specific constraint that we needed to consider in our project was about the test users.

To execute scenarios in parallel in our PCAS application, we needed a different user for each worker. This is to avoid conflicts among scenarios execution.

We found that 9 parallel workers were a good target to have an acceptable execution time, and therefore we created 9 test users in the application, one for each of the needed “role.” Since we needed 2 roles (front-office and back-office), we created 18 test users.17

The credentials of these test users are stored in a data object at project level ($project.userListFO and $project.userListBO).

In the Login test, we then use function in Boozang called getRealCoopKey() which returns the ID of the worker:
$test.worker = $project.getRealCoopKey();
$util.addLogData({"worker":$test.worker});
Based on the role that is passed as parameter, we then select the correct credentials and make sure that each worker runs with a different user:
switch($parameter) {
  case "frontoffice":
    console.log("BZ-LOG: running branch front office")
    $test.username = $project.userListFO[$test.worker].username;
    console.log("BZ-LOG: username" + $test.username)
    $test.password = $project.userListFO[$test.worker].password;
    break;
  case "backoffice":
    console.log("BZ-LOG: running branch back office")
    $test.username = $project.userListBO[$test.worker].username;
    console.log("BZ-LOG: username" + $test.username)
    $test.password = $project.userListBO[$test.worker].password;
    break;
  default:
    console.log("BZ-LOG: don't know what to do with role: " + $parameter);
}

Set Variables

In this section we show the variables defined for later use.
//use the Golive shared library
@Library('apwide-jenkins-shared-lib') _
// _____________________Pipeline Parameters_________________________________________________
def agent_worker_name = env.AGENT_IMAGE?env.AGENT_IMAGE:"jenkins-agent-boozang" //Jenkins agent Image (defined in Jenkins Configuration)
def debug=env.DEBUG?env.DEBUG:false // Force this pipeline agent in sleep loop, enabling ssh inside this current agent where command are executed
// ____________________________________ Pipeline Variables__________________________________________
def reportsDir = "/tmp/boozang/$JOB_NAME/$BUILD_ID" // Container folder for reports
def lge3as255pDir = "/opt/data_unsecured/jenkins/boozang/$JOB_NAME/$BUILD_ID" // Host folder for reports
def cucumberReport = 'results.json'
def imageName = "styrman/boozang-runner:"
def token = "xxxxxxxxx"  // Masked, to replace with your own oken
def baseurl = "http://eu.boozang.com"
def project = "5e60f3e3deb43b53367fb106"
def self = 0 //id used to isolate CI executions from local ones.
def group = "$BUILD_ID" //isolate concurrent boozang jobs
def env = env.BGENVIRONMENT //the environment is defined in the job
def jira_environment
def jira_version

We will use these variables in the following stages.

Define Job Parameters

The following code blocks define the parameters that can be set when launching the job. This is very important for a job that you will launch manually, and much less for nightly schedules.
pipeline {
    agent { label agent_worker_name }
    parameters {
        booleanParam(name: 'DEBUG',
                defaultValue: 'false',
                description: 'Parameter used to activate debug on pipeline, This parameter will print all job environment, and end your job in infinite loop allowing you to ssh inside it the debug commands or configuration'
                )
        string (name: 'TEST_ID',
                defaultValue: 'm74/t1',
                description: 'Suite or scenario ID, format mxx/txx '
        )
        string (name: 'BRANCH',
                defaultValue: 'master',
                description: 'Branch to be used for running the tests'
        )
        booleanParam(name: 'SMOKE_TESTS',
                defaultValue: 'false',
                description: 'Check to run smoke tests before the rest'
        )
        choice(name: 'WORKERS',
                choices: ['9','8','7','6','5','4','3','2','1'],
                description: 'Select the number of workers to use'
        )
        string (name: 'JIRALABEL',
                defaultValue: 'nista-acp-nightly-full',
                description: 'Attach a specific label to the Test Execution in JIRA'
        )
        string (name: 'JOB_DESCRIPTION',
                defaultValue: 'direct launch',
                description: 'Description to set for the build'
        )
        string (name: 'BZBASEURL',
                defaultValue: 'eu.boozang.com',
                description: 'Boozang base URL'
        )
        string (name: 'BZTOKEN',
                defaultValue: '555203d19a3caa582ea45ac9e6470eeb5e60f3e3deb43b53367fb106',
                description: 'Boozang token'
        )
        string (name: 'BZPROJECT',
                defaultValue: '5e60f3e3deb43b53367fb106',
                description: 'Boozang project'
        )
        string (name: 'BZIDE_RELEASE',
                defaultValue: 'Standard',
                description: 'Boozang IDE release used to run the job'
        )
        string (name: 'CONTAINER',
                defaultValue: '2.1.16',
                description: 'Container version used by the runner'
        )
    }

All these parameters give us great flexibility to use this pipeline for different purposes, and also adapt to Boozang releases in case of issues or beta features.

Set Build Information

When you work in a team, you might have several jobs running in your Jenkins project. It is a good practice to edit the build description so that you can find your build later and align also the other team members.

We decided to set the build description with a default test (one can change this in the parameters) and then the ID of the test launched plus the environment ID.
/***********************************************************************
         *  Set some information in the job description
************************************************************************
        stage('Set build info') {
             steps {
                script {
                     currentBuild.description = "${params.JOB_DESCRIPTION} ${params.TEST_ID} Env: ${env}"
                }
             }
        }

Execute Smoke Tests

This is a step that can be skipped and is used to execute some smoke tests before launching the specified suite. It is useful when the suite launched is long, and you want to avoid failing all tests for an environment-related issue. In this case, you want to fail fast without running the rest of the pipeline.

In this stage we call the Boozang runner directly without any worker setup, since this is a quick test and does not need to run in parallel.
/**************************************************************************
         *  Launch the simple login tests on all the applications
 *************************************************************************/
        stage('Smoke connections') {
            when { expression { params.SMOKE_TESTS } }
             steps {
                script {
                     sh "docker run --rm  -v "${lge3as255pDir}:/var/boozang/" -v "${lge3as255pDir}/common:/var/common/" $imageName$params.CONTAINER --file=smoke "https://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${params.BGENVIRONMENT}#${params.BZPROJECT}/${params.BRANCH}/m74/t2/run""
                }
            }
        }

Launch Boozang Workers and Master

The master-slaves concept has been introduced in section “Boozang Workers Setup in the Jenkins Pipeline.”

One more thing to notice in the following stage is that we want to avoid having the same worker ID for jobs running in different environments. We therefore decided to use the following setup:

QA:
  • Master key 1

  • Workers keys 2-9

ACP:
  • Master key 11

  • Workers keys 12-19

/**************************************************************************
         *  Launch the scenario/suite with distributed workers
**************************************************************************/
        stage('Boozang distributed run') {
            steps {
                script {
                    echo "Setting up boozang slaves..."
                    def counter = 1
                    def worker_key = counter
                    def master_key = 1
                    if (env == '1') {
                        //spread workers id across environments
                        worker_key = counter + 10
                        echo "Running on ACP"
                    }
                    else {
                        echo "Running on QA"
                    }
                    echo "workers:  $params.WORKERS"
                    Integer w = params.WORKERS as Integer
                    sh "mkdir -p ${reportsDir}"
                    while (counter < w) {
                        counter++
                        worker_key++
                        echo "worker: $counter"
                        echo "Launching worker with key:" + "${worker_key}"
                        if (params.BZIDE_RELEASE == "Standard") {
                            worker_url = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$worker_key&self=${self}#${params.BZPROJECT}/${params.BRANCH}"
                        } else {
                            worker_url = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$worker_key&self=${self}&ide=${params.BZIDE_RELEASE}#${params.BZPROJECT}/${params.BRANCH}"
                        }
                        sh "nohup docker run --name bg-worker$counter -e TZ=Europe/Paris --rm -v "${lge3as255pDir}:/var/boozang/" -v "${lge3as255pDir}/common:/var/common/" ${imageName}${params.CONTAINER} "$worker_url" >  ${reportsDir}/boozang-worker_${worker_key}.log &"
                        sleep(5)
                    }
                    echo "All boozang slaves started"
                    echo "Launching boozang master..."
                    if (env == '1') {
                        master_key = master_key + 10
                        echo "Running on ACP"
                    }
                    if (params.BZIDE_RELEASE == "Standard") {
                        master_URL = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$master_key&self=${self}#${params.BZPROJECT}/${params.BRANCH}/${params.TEST_ID}/run"
                    } else {
                        master_URL = "http://${params.BZBASEURL}/extension?token=${params.BZTOKEN}&env=${env}&group=${group}&key=$master_key&self=${self}&ide=${params.BZIDE_RELEASE}#${params.BZPROJECT}/${params.BRANCH}/${params.TEST_ID}/run"
                    }
                    try {
                        sh "docker run --name bg-master -e TZ=Europe/Paris --rm -v "${lge3as255pDir}:/var/boozang/" -v "${lge3as255pDir}/common:/var/common/" ${imageName}${params.CONTAINER} "$master_URL" >  ${reportsDir}/boozang-master.log"
                        echo "Well done, no errors!"
                        currentBuild.result = 'SUCCESS'
                    }
                    catch (err) {
                        echo "There were functional failures in the executed tests: ${err}"
                        currentBuild.result = 'UNSTABLE'
                    }
                }
            }
        }

Aggregate JSON Reports and Log Files

As previously mentioned, the aggregation of JSON files is necessary to send a single JSON to XRay to import results into a new Test Execution. We also aggregate the log files to be able to use the handy log formatter feature (see section “Log Files and the Log Formatter”).
/**************************************************************************
         *  json reports aggregation
**************************************************************************/
        stage('Aggregate Cucumber json results') {
            steps {
                sh "sudo chown -R jenkins:jenkins ${reportsDir} && cucumber-json-merge -d "${reportsDir}" -o "${reportsDir}/${cucumberReport}" && echo $?"
            }
  }
/**************************************************************************
         *  Log files aggregation   **************************************/
        stage('Aggregate Log files') {
            steps {
                echo "Aggregating Log files..."
                script {
                    try {
                        sh "cat ${reportsDir}/boozang-*.log > ${reportsDir}/merged-${group}.log"
                    }
                    catch (err) {
                        echo "Error executing the Aggregate Log files stage"
                        currentBuild.result = "UNSTABLE"
                    }
                }
            }
        }

Download and Execute RCA Script

The RCA script stage was already mentioned in section “Root Cause Analysis Report.” We report the stage once more as follows for convenience.
/**************************************************************************
         *  Root Cause Analysis
**************************************************************************/
        stage('Perform RCA') {
            steps {
                echo "Analyze root causes..."
                script {
                    try {
                        sh "sudo apt-get -yq install jq"
                        sh "jq --version"
                        sh "sudo apt-get -yq install bc"
                        sh "bc --version"
                        sh "curl -o ${reportsDir}/generate_summary.sh https://raw.githubusercontent.com/ljunggren/bz-utils/main/scripts/generate_summary.sh"
                        echo "RCA script downloaded"
                        sh "chmod +x ${reportsDir}/generate_summary.sh"
                        echo "Rights adapted"
                        sh "${reportsDir}/generate_summary.sh ${reportsDir}/${cucumberReport} > ${reportsDir}/rca.txt | echo 'Script executed'"
                        sh "cp bz-report* ${reportsDir}"
                    }
                    catch (err) {
                        echo "Error executing the RCA stage"
                        currentBuild.result = "UNSTABLE"
                    }
                }
            }
        }

Publish Cucumber Reports

The stage to publish Cucumber reports via the Cucumber Report Jenkins plugin was already mentioned in section “Cucumber Reports.” We report the stage once more as follows for convenience.
/**************************************************************************
         *  Collect the results with the cucumber report publisher plugin
         *****************************************************************/
        stage('Prepare Jenkins Cucumber reports') {
            steps {
                step([$class: 'CucumberReportPublisher',
                      buildStatus: 'UNSTABLE',
                      failedFeaturesNumber: 0,
                      failedScenariosNumber: 0,
                      failedStepsNumber: 0,
                      fileExcludePattern: "${cucumberReport}",
                      fileIncludePattern: "*.json",
                      jsonReportDirectory: "${reportsDir}",
                      pendingStepsNumber: 0,
                      skippedStepsNumber: 0,
                      mergeFeaturesById: true,
                      mergeFeaturesWithRetest: false,
                      undefinedStepsNumber: 0])
            }
        }

Get Currently Deployed Version

In this stage, via the APWide GoLive plugin,18 we retrieve the version currently deployed in the test environment, be it QA or ACP. This will be used later on when importing results into Xray.
/**************************************************************************
         *  Get the IDIT version currently deployed on ACP
         *****************************************************************/
        stage('Get deployed version') {
            environment {
                APW_JIRA_BASE_URL = 'https://jira.tcsgroup.ch/'
                APW_JIRA_CREDENTIALS_ID = 'xxxxx'
                APW_APPLICATION = 'IDIT'
                APW_CATEGORY = 'ACP'
            }
            steps {
                script {
                    jira_environment = apwGetEnvironment()
                    jira_version = jira_environment.deployment.versionName
                    echo jira_version
                }
            }
   }

Import Results into XRay

The import of execution results into XRay via the XRay Jenkins plugin was covered already in Chapter 14, “JIRA with Boozang via XRay.” We report the stage once more as follows for convenience.
/**************************************************************************
         *  Upload cucumber results to Jira
*************************************************************************/
        stage('Jira: Import execution results') {
            steps {
                step([$class: 'XrayImportBuilder',
                        endpointName: '/cucumber/multipart',
                        importFilePath: "${reportsDir}/${cucumberReport}",
                        importInfo: """{
                                            "fields": {
                                                "project": {
                                                    "key": "IDITS"
                                                },
                                                "summary": "$JOB_NAME build $BUILD_ID",
                                                "description" : "$BUILD_URL/cucumber-html-reports/overview-features.html",
                                                "labels": ["${params.JIRALABEL}"],
                                                "versions":  [{ "name": "$jira_version"}],
                                                "customfield_10516": ["IDIT_ACP"],
                                                "issuetype": {
                                                    "id": "10106"
                                                }
                                            }
                        }""",
                        inputInfoSwitcher: 'fileContent',
                        serverInstance: 'SERVER-e427f454-1c58-44d0-b99a-196bf141911b'
                ])
            }
        }
    }

HTML Publisher

This stage was already shown in section “HTML Reports.” We report it once more as follows for convenience .
post {
        always {
            //publish worker logs
            publishHTML target : [allowMissing: false,
            alwaysLinkToLastBuild: true,
            keepAll: true,
            reportDir: "${reportsDir}",
            reportFiles: '**/*.log',
            reportName: 'Worker Logs',
            reportTitles: '']
            //publish json reports
            publishHTML target : [allowMissing: false,
            alwaysLinkToLastBuild: true,
            keepAll: true,
            reportDir: "${reportsDir}",
            reportFiles: '**/*.json',
            reportName: 'Cucumber Json files',
            reportTitles: '']
            //publish root cause report
            publishHTML target : [allowMissing: false,
            alwaysLinkToLastBuild: true,
            keepAll: true,
            reportDir: "${reportsDir}",
            reportFiles: '**/*.html',
            reportName: 'RCA report',
            reportTitles: '']
            script {
                if (params.DEBUG == true)
                    sh "sleep 999999"
            }
        }
    }
} //Pipeline end :)

This concludes the pipeline code and this chapter.

This working example is specific to our environment. However, I believe it can help a lot of readers to get ideas and solve problems.

Docker in Docker Setup

In our Jenkins setup, we have a dedicated linux host on a virtual machine, with a docker engine running on it. Jobs run on this slave but with a docker agent created on the fly,19 and destroyed when the build is finished. The image is an Ubuntu image with a few more packages installed, hence a dedicated boozang-image.

In the dockerized Jenkins boozang-agent , we then execute the pipeline, which then launches the actual boozang master and workers.

In this “docker in docker” setup, a correct setup of the volumes is critical. The following picture summarizes the setup.

A flow diagram of the docker setup in Jenkins. The job is executed on Jenkins master and slave with volume setup for agent, master, and worker.

Jenkins Docker in Docker setup

The Boozang runner containers store report files in /var/boozang. We mapped this folder to /tmp/boozang on the Jenkins boozang-agent . In order to save those reports permanently, we then map the /tmp/boozang folder to a local folder on the physical host, where enough space is available.

It is worth mentioning that during remote executions via Jenkins, the currently active workers are visible in the UI. The following two screenshots have been taken at two different times during the execution of a scenario outline.

A screenshot of a popup Boozang window with an execution worker list. The window has configuration items and a tab to generate C I, U R L.

CI view at the beginning of the job

A screenshot of a popup Boozang window during worker execution. It lists the workers and provides updates on success, failure, testing, and waiting.

CI view during workers execution

While setting up your Jenkins pipeline, you might need to check directly on the containers what is happening. Especially to make sure that volumes are correctly mapped and that access permissions are correctly set.

If you are not too familiar with the Docker CLI, a tool like Portainer20 can help. Without going into too much detail, Portainers allow you to see the images available, volumes and networks, and especially the running containers.

A screenshot of a Portainer window. The dashboard provides an endpoint summary of stacks, containers, images, volumes, and networks.

Portainer UI

When the containers are running, you can list them, enter the container shell, and see what is going on in case of issues .

A screenshot of the container section on Portainer dot i o. The section lists container I D, images, commands, dates, status, ports, and names.

Boozang workers view in Portainer

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.223.208