To keep track of new Jira version releases and testing the new versions becomes a lot handier if an instance of the new Jira version is started automatically as soon as it is released. The setup presented in the following does exactly that. Using a daily running Selenium script checking the Jira download website, Jira releases are detected. This triggers the built of a new Jira image and a docker container is run from the image. Another Selenium script performs the setup of Jira using the setup wizard. In the end, a notification is sent to a slack channel containing the URL and the credentials for your new Jira instance.

Overview

The infrastructure setup

Development server

On the development server the Bamboo, Bamboo agent, Bitbucket and docker registry are hosted.

Bamboo and Bamboo agent

Bamboo is a built server which I used to automate the whole process from detecting a new Jira version to running the new Jira container. This process is configured in a Bamboo plan. Every step is defined in a task, multiple tasks are grouped in plans/stages and multiple stages produce the whole plan.

A Bamboo agent is a service that provides the capability to run these tasks. I used a Bamboo remote agent docker container, which I configured to provide all capabilities needed to perform the tasks we want to use.

Build/run/start docker containers

In order to give your Bamboo agent the capability to build/run/start docker containers the easiest way is to expose the docker socket to the Bamboo agent container by bind-mounting it with the --mount flag when running the agent container. This enables the Bamboo agent to start sibling containers. If you wonder why you should not just start child docker containers in the Bamboo agent docker container check out this article about docker-in-docker.

In your Bamboo agent dockerfile include the following lines:

RUN groupadd -g 999 docker \
    && useradd -r -u <user_id> -g docker <user_name>

RUN curl -sSL https://get.docker.com/ | sh

These lines create the docker group, add the Bamboo agent user to this group and install docker. This will enable the Bamboo agent user to build/run/start docker containers. For the user_id and the user_name use the user that runs the Bamboo agent docker container (this should not be root).

Shared variables

The Bamboo agent needs access to variables, whose values are determined in tasks running in sibling docker containers. For example, the Selenium script that checks for the newest Jira version, is executed in a Selenium docker container. To share this jira_newest-version variable with the Bamboo agent I created a variable file on the host that is bind-mounted into both the Bamboo agent and the Selenium docker container, so both containers can write to and read from this variable file.

Write slack messages

Add the following line to your Bamboo agent dockerfile to enable it to write slack messages using the slack-cli.

RUN apt-get -y install nodejs \
    && apt-get -y install npm \
    && npm install -g slack-cli

Execute python scripts

Some tasks I implemented are using python scripts. Add this to enable the Bamboo agent to execute python scripts.

RUN apt-get -y install python3

Your Bamboo agent dockerfile could look like this

FROM adoptopenjdk/openjdk8

#install packages and create user
RUN apt-get update \
    && apt-get -y install wget \
    && apt-get -y install curl \
    && apt-get -y install python3 \
    && apt-get -y install nodejs \
    && apt-get -y install npm \
    && npm install -g slack-cli \
    && groupadd -g 999 docker \
    && useradd -r -u 1022 -g docker bamboo

#install docker
RUN curl -sSL https://get.docker.com/ | sh

# Make directory to save bamboo agent installer .jar and create bamboo-agent-home
RUN set -x \
    && mkdir -p                             "/var" \
    && chmod -R 700                         "/var" \
    && chown -R bamboo:docker               "/var" \
    && mkdir -p                             "/home/bamboo/bamboo-agent-home" \
    && chmod -R 700                         "/home/bamboo/bamboo-agent-home" \
    && chown -R bamboo:docker               "/home/bamboo/bamboo-agent-home" 

# Copy .jar and entrypoint to container
COPY atlassian-bamboo-agent-installer-6.8.0.jar /var
COPY entrypoint.sh /

# Make .jar and entrypoint executable
RUN set -x \
    && chmod -R 700                         "/var/atlassian-bamboo-agent-installer-6.8.0.jar" \
    && chown -R bamboo:docker               "/var/atlassian-bamboo-agent-installer-6.8.0.jar" \
    && chmod -R 700                         "/entrypoint.sh" \
    && chown -R bamboo:docker               "/entrypoint.sh" 

USER bamboo

EXPOSE 8060
    
WORKDIR /var 

ENTRYPOINT ["/entrypoint.sh"]

With the following entrypoint.sh

#!/bin/bash

cd /var
java -jar atlassian-bamboo-agent-installer-6.8.0.jar "https://<bamboo_url>/agentServer/"

exec "$@"

The bamboo_url has to be the URL of your Bamboo instance.

The atlassian-bamboo-agent-installer-6.8.0.jar has to be generated from your Bamboo instance (follow this guide steps 2.2-2.6) and be saved on the host in the same directory as your dockerfile and entrypoint.sh.

Build an image from this dockerfile and entrypoint.sh and run a container from the image.

docker build -t bamboo_agent_example:1.0 .
docker run -d  \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,readonly \
--mount type=bind,source=/var/docker_apps/bamboo_variables/variables.properties,target=/var/variables.properties \
--name bamboo_agent_example_container \
bamboo_agent_example:1.0

To setup the connection from the remote agent to your Bamboo instance authenticate the Bamboo agent. You can find the agent’s unique id in the docker logs of the Bamboo agent container to verify you authenticate the correct agent.

docker logs bamboo_agent_example_container

Bitbucket

I use a Bitbucket repository to store all code that I want to use in the Bamboo plan. This repository can be added to the Bamboo plan so Bamboo has access to the newest commits. Read here about how to integrate Bamboo with Bitbucket.

docker registry

The docker registry I use to store images and make them available outside the development server. I set up the docker registry using the registry image.

Test server

On the test server the test instances of the new Jira images are run.

Nginx and Let’s Encrypt

In order to start new Jira containers behind a Nginx proxy and with SSL configured I use the jwilder/nginx-proxy and the JrCs/docker-letsencrypt-nginx-proxy-companion docker container. As the host docker socket is bound inside these containers they can react to the start/termination of new containers by automatically updating and reloading nginx config files and creating/renewing Let’s Encrypt certificates.

SSH access to test server

To make the Bamboo agent able to start docker containers on the test server, I use the Bamboo ssh task. To prepare this, the Bamboo user (running the Bamboo agent) has to be created on the test server, be a member of the docker group and the according public key has to be deposited. The according private key needs to be uploaded to the Bamboo agent when using the “ssh task” to access the test server.

Create the Bamboo plan

In Bamboo create a new plan and connect it to your Bitbucket repository. Decide to run this job in an agent environment so the remote agent docker container will be used. You can configure a trigger to decide when the plan shall be run. To keep track of new Jira version releases a daily execution is reasonable.

Stage 1: Detecting Jira version release

To detect a Jira version release a Selenium script is run as entrypoint in a Selenium docker container. The Selenium docker container contains an installation of headless chrome which allows running chrome in a server environment. The Selenium Script opens the Jira download website in headless chrome and checks the version of the newest Jira release which is saved on the host in the bind mount file variables.properties.

As a base image for the Selenium docker Container I used selenium/standalone-chrome.

For the Selenium setup this guide was helpful to me.

Create the following files in your Bitbucket repository in a subdirectory.

dockerfile for Selenium docker container

FROM selenium/standalone-chrome:3.141.59-titanium

USER root

# create project folder with the name code
RUN mkdir /code 

RUN set -x &&\
  apt-get         -y update && \
  apt-get install -y apt-utils && \
  apt-get install -y python-pip && \
  apt-get install -y libxml2-dev libxslt1-dev libxml2 && \
  apt-get install -y python-setuptools

# install requirements
COPY requirements.txt .

RUN pip install -r requirements.txt

USER seluser

# project scope
WORKDIR /code

# Set docker entry
COPY lotte-checkjiraversion_remotedriver.py /code/
ENTRYPOINT ["python", "/code/lotte-checkjiraversion_remotedriver.py"]

requirements.txt

arrow==0.8.0
colorama==0.3.7
enum34==1.1.6
lxml==3.6.0
moment==0.5.1
namedlist==1.7
py==1.4.31
pytest==2.7.3
pytest-allure-adaptor==1.7.7
pytest-gitignore==1.3
python-dateutil==2.5.3
pytz==2016.4
selenium==2.53.5
six==1.10.0
times==0.7

lotte-checkjiraversion_remotedriver.py

This script checks for the newest available Jira Software version and creates/updates the according variable jira_newest_version in the bind mount file variables.properties.

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException  
from selenium.webdriver.chrome.options import Options

print("This script checks for the newest available Jira Software version.")
print("The newest Jira Software version is:")

#URL
jira_update_url = "https://www.atlassian.com/software/jira/update"

#enable options to use headless chrome
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')

# Create a new instance of the chrome driver
driver = webdriver.Chrome(executable_path = '/usr/bin/chromedriver', chrome_options=chrome_options)

# Define waits
wait = WebDriverWait(driver, 10)

try:
    #go to Jira update page
    driver.get(jira_update_url)

    # find the element that displays the version name
    inputElement = wait.until(EC.element_to_be_clickable((By.ID,'get-started')))
    inputElement = driver.find_element_by_id('get-started')

    #get the version name as variable
    versiontext = inputElement.get_attribute('data-minor-version')

    print(versiontext)

    #open file to read variable inside
    with open("/code/`variables.properties`", "r") as f:
        variable_list = f.readlines()

    # Is the variable already in the list and up to date? If not add it or replace the old version.

    variable_actual = False
    variable_exists = False

    for line in variable_list:
        if "jira_newest_version=" + versiontext in line:
            variable_actual = True
            variable_exists = True
            
    print("The version variable in the file `variables.properties` exists and is actual: " + str(variable_actual))

    if not variable_actual:
        for i, item in enumerate(variable_list):    
            if "jira_newest_version=" in variable_list[i]:
                newline = "jira_newest_version=" + versiontext + "\n"
                variable_list[i] = newline
                variable_exists = True
        with open("/code/`variables.properties`", "w") as f:
            f.writelines(variable_list)
                
        print("The version variable in the file `variables.properties` exists but is not actual: " + str(variable_exists) + ".")
        
        if variable_exists:
            print( "It was updated to version " + versiontext + ".")

        if not variable_exists:
            with open("/code/`variables.properties`", "a") as f:
                f.write("jira_newest_version=" + versiontext + "\n")
            print("The version variable in the file `variables.properties` does not exist and was appended.")

    #open file to read variables inside
    with open("/code/`variables.properties`", "r") as f:
        variable_list = f.readlines()
        print("This is the actual list of variables" + str(variable_list))

finally:
    driver.quit()

docker task

In Bamboo the docker task can be used to build a docker image from a dockerfile saved in a subdirectory in your Bitbucket repository. The subdirectory has to be specified in the tasks advanced options. In a second docker task a container can be run from this image, which will check the newest available Jira version and write it to the variables.properties file. For this, the following additional options are needed:

  • Container environment variables: PYTHONPATH=/code/
  • Container working directory: /code
  • Additional arguments:
     --mount 'type=bind,source=/var/docker_apps/bamboo_variables/variables.properties,dst=/code/variables.properties' 

Inject Bamboo variables and script task

To make the variable jira_newest_version (and other variables in variables.properties) available in the Bamboo plan it has to be injected from the bind mount file variables.properties to the Bamboo variables. The task “Inject Bamboo variables” is used for this. It loads all variables defined in a file of the following format.

variable1=value
variable2=value

When loading the variables from a file, a namespace has to be defined to avoid conflicts with existing variables. I used inject as namespace so the jira_newest_version variable can be accessed using ${bamboo.inject.jira_newest_version}.

In the variables.properties file I defined another variable called jira_newest_image_version in which the Jira software version of the newest existing image is saved. To start, you can set the jira_newest_image_version by hand to any version below the jira_newest_version, later on it will be updated by Bamboo. The idea is that a new image is built when the jira_newest_image_version does not match the jira_newest_version.

Unfortunately, the add-on Bamboo conditional tasks does not support conditional tasks based on the comparison of two variables but only based on the value of one variable. This is why I created a third variable jira_is_not_up_to_date which is set by a python script and results in True if jira_newest_image_version != jira_newest_version and in False if jira_newest_image_version = jira_newest_version. The python script is located in the Bitbucket repository and can be executed using the Bamboo script task.

Python script to set jira_is_not_up_to_date

print("This script updates variables.properties to contain the correct version of the variable jira_is_not_up_to_date")

#open file to read variable inside
with open("/var/variables.properties", "r") as f:
    variable_list = f.readlines()

#Check if variable jira_is_not_up_to_date exists

variable_actual = False
variable_exists = False


for line in variable_list:
    if "jira_is_not_up_to_date=" + var_jira_is_not_actual in line:
        variable_exists = True
        variable_actual = True
        
print("The jira_is_not_up_to_date variable in the file variables.properties exists and is actual: " + str(variable_actual))


if not variable_actual:
    for i, item in enumerate(variable_list):    
        if "jira_is_not_up_to_date=" in variable_list[i]:
            newline = "jira_is_not_up_to_date=" + var_jira_is_not_actual + "\n"
            variable_list[i] = newline
            variable_exists = True
    with open("/var/variables.properties", "w") as f:
        f.writelines(variable_list)
            

    print("The jira_is_not_up_to_date variable in the file variables.properties exists but is not actual: " + str(variable_exists) + ".")
    
    if variable_exists:
        print( "It was updated to " + var_jira_is_not_actual + ".")

    if not variable_exists:
        with open("/var/variables.properties", "a") as f:
            f.write("jira_is_not_up_to_date=" + var_jira_is_not_actual + "\n")
        print("The jira_is_not_up_to_date variable in the file variables.properties does not exist and was appended.")

#open file to read variables inside
with open("/var/variables.properties", "r") as f:
    variable_list = f.readlines()
    print("This is the actual list of variables" + str(variable_list))

By using the checkbox “conditional task”, the following tasks are only executed if jira_is_not_up_to_date=True.

Every time a variable in variables.properties is newly created or updated, the “inject Bamboo variables” task has to be run again to make the new variable available to Bamboo. Bamboo does not keep track of changes in a variable file injected by this task.

Stage 2: Build Jira image and push to docker registry

As we create our own Jira images we have a standard dockerfile and entrypoint.sh for Jira Software images. These files are loaded to the bamboo agent from the connected Bitbucket repository using the “Source Code Checkout” task. The Jira version is defined as environmental variable in the dockerfile, so we simply set the Jira version to the jira_newest_version when building the new Jira image with a Bamboo docker task.

Atlassian started to provide docker images for Jira software. To create your dockerfile/entrypoint.sh you can clone or orient by these.

Using another docker task the new Jira image can be pushed to the docker registry.

Stage 3: Run Jira Container

Set DNS entry

For the new Jira instance I set a DNS entry first. If you use GoDaddy DNS records, this python script, saved in your Bitbucket repository, can simplify the procedure. Using a Bamboo script task with the following script body will produce a DNS entry in the format testjira8-0-5.anarcon.net.

export JIRANEWESTVERSION=$(echo ${bamboo.inject.jira_newest_version} | tr . -)
python3 dns_changer.py --key <key> --secret <secret> --ip <ipaddress> testjira${JIRANEWESTVERSION}.anarcon.net

Run the container on your test server

In an SSH task connect to your test server by specifying its IP address and the connecting username bamboo and uploading the private SSH key. In the ssh command run a container from the image. For the Nginx and Let’s Encrypt containers to be notified of the new container, define the environmental variables VIRTUAL_HOST and LETSENCRYPT_HOST.

export JIRANEWESTVERSION=$(echo ${bamboo.inject.jira_newest_version} | tr . -)
export JIRAURL="testjira${JIRANEWESTVERSION}.anarcon.net"

docker run -d --name test_jira_$JIRANEWESTVERSION \
-e "VIRTUAL_HOST=${JIRAURL}"  \
-e "LETSENCRYPT_HOST=${JIRAURL}"  \
-e "VIRTUAL_PORT=8080"  \
-e "LETSENCRYPT_EMAIL=<your email address>"  \
--mount "type=volume,src=test_jira_${JIRANEWESTVERSION}_home_dir,dst=<home directory path in your container>"  \
"<link to your Jira docker image in the docker registry>"

Stage 4: Setup Jira and notify via Slack

Setup Jira

To setup Jira, a similar procedure as in Stage 1 is followed. In a docker task an image is built in which the entrypoint contains the python Selenium script, that connects via headless chrome to the Jira instance and completes the setup wizard. A second docker task runs a container from the image. The Dockerfile/entrypoint are located in a subdirectory of the Bitbucket repo and are loaded to Bamboo by a “Source Code Checkout” task.

To customize the python script for every Jira version and to avoid storing the credentials for the Jira instance in the python script, I used variables of the syntax replace_jira_url. Before the docker image is built, these variables are replaced in the python entrypoint script by environmental variables defined in variables.properties and in the variables section of the Bamboo Plan Configuration. The replacement is implemented in a script task with the working directory set to the Bitbucket subdirectory where the Dockerfile/entrypoint are located.

The script body is

export JIRANEWESTVERSION=$(echo ${bamboo.inject.jira_newest_version} | tr . -)
export JIRAURL="https://testjira${JIRANEWESTVERSION}.anarcon.net"

sed -i 's@replace_jira_url@'$JIRAURL'@g' lotte-setupjira_remotedriver.py
sed -i 's@replace_jira_version@'${bamboo.inject.jira_newest_version}'@g' lotte-setupjira_remotedriver.py
sed -i 's@replace_jira_fullname@'${bamboo.JiraFullName}'@g' lotte-setupjira_remotedriver.py
sed -i 's/replace_jira_email/'${bamboo.JiraEmail}'/g' lotte-setupjira_remotedriver.py
sed -i 's@replace_jira_username@'${bamboo.JiraUsername}'@g' lotte-setupjira_remotedriver.py
sed -i 's@replace_jira_password@'${bamboo.JiraPassword}'@g' lotte-setupjira_remotedriver.py

The requirement.txt file is the same as in stage 1 and in the Dockerfile only the python script name has to be changed to load the right entrypoint. This is the python script setting up Jira.

#lotte-setupjira_remotedriver.py
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException  
from selenium.webdriver.chrome.options import Options

print("This script sets up a new Jira instance, creates a test project and checks if the creation of an issue works")

jira_url = "replace_jira_url"
jira_version = "replace_jira_version"

#admin account
jira_fullname = "replace_jira_fullname"
jira_email = "replace_jira_email"
jira_username = "replace_jira_username"
jira_password = "replace_jira_password"
jira_license = '''<your Jira license>'''

chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')

# Create a new instance of the chrome driver
driver = webdriver.Chrome(executable_path = '/usr/bin/chromedriver', chrome_options=chrome_options)

wait = WebDriverWait(driver, 10)
waitlong = WebDriverWait(driver, 1000)

try:
    #go to empty Jira setup wizard
    driver.get(jira_url)

    # find the element that displays "I'll set it up myself"
    path = "//form[@id='jira-setup-mode']/div[2]"
    waitforelement = waitlong.until(EC.element_to_be_clickable((By.XPATH,path)))
    inputElement = driver.find_element_by_xpath(path)
    inputElement.click()

    #submit the form
    inputElement = driver.find_element_by_id("jira-setup-mode-submit")
    inputElement.click()

    #choose Built in database and continue
    waitforelement = wait.until(EC.element_to_be_clickable((By.ID,"jira-setup-database-submit")))
    inputElement = driver.find_element_by_id("jira-setup-database-submit")
    inputElement.click()

    #choose application title, mode and base url
    waitforelement = waitlong.until(EC.element_to_be_clickable((By.ID,"jira-setupwizard-submit")))
    inputElement = driver.find_element_by_name("title")
    inputElement.send_keys("Jira Test " + jira_version)
    print( "The application title is " + inputElement.get_attribute('value'))
    inputElement = driver.find_element_by_id("jira-setupwizard-submit")
    inputElement.click()

    #insert license
    waitforelement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".aui-button.aui-button-primary")))
    inputElement = driver.find_element_by_name("licenseKey")
    inputElement.send_keys(jira_license)
    print( "The used license is \n"  + inputElement.get_attribute('value'))
    inputElement = driver.find_element_by_css_selector(".aui-button.aui-button-primary")
    inputElement.click()

    #wait until license is applied and set up admin account
    waitforelement = waitlong.until(EC.element_to_be_clickable((By.ID,"jira-setupwizard-submit")))
    print( "Setup administrator account")

    inputElement = driver.find_element_by_name("fullname")
    inputElement.send_keys(jira_fullname)
    print( "The full name is "  + inputElement.get_attribute('value'))
    
    inputElement = driver.find_element_by_name("email")
    inputElement.send_keys(jira_email)
    print( "The email is "  + inputElement.get_attribute('value'))

    inputElement = driver.find_element_by_name("username")
    inputElement.send_keys(jira_username)
    print( "The username is "  + inputElement.get_attribute('value'))

    inputElement = driver.find_element_by_name("password")
    inputElement.send_keys(jira_password)

    inputElement = driver.find_element_by_name("confirm")
    inputElement.send_keys(jira_password)

    inputElement = driver.find_element_by_id("jira-setupwizard-submit")
    inputElement.click()

    #setup no email notifications
    waitforelement = wait.until(EC.element_to_be_clickable((By.ID,"jira-setupwizard-submit")))
    inputElement = driver.find_element_by_id("jira-setupwizard-submit")
    inputElement.click()

    #setup language
    waitforelement = waitlong.until(EC.element_to_be_clickable((By.ID,"next")))
    inputElement = driver.find_element_by_xpath("//label[@for='locale_en_UK']/parent::*")
    inputElement.click()
    inputElement = driver.find_element_by_id("next")
    inputElement.click()   

    #Do not choose an avatar
    waitforelement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".aui-button.avatar-picker-done")))
    inputElement = driver.find_element_by_css_selector(".aui-button.avatar-picker-done")
    inputElement.click()  

    #create empty project
    waitforelement = wait.until(EC.element_to_be_clickable((By.ID,"emptyProject")))
    inputElement = driver.find_element_by_id("emptyProject")
    inputElement.click()
    waitforelement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".create-project-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")))
    inputElement = driver.find_element_by_css_selector(".create-project-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")
    inputElement.click()
    waitforelement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".template-info-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")))
    inputElement = driver.find_element_by_css_selector(".template-info-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")
    inputElement.click()
    waitforelement = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,".add-project-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")))
    inputElement = driver.find_element_by_id("name")
    inputElement.send_keys("test project")
    inputElement = driver.find_element_by_css_selector(".add-project-dialog-create-button.pt-submit-button.aui-button.aui-button-primary")
    inputElement.click()


finally:
    driver.quit()

I defined the Jira license in the python script because the multiline format of the license could not be implemented as variable in the Bamboo Plan Configuration.

Note: The html syntax of the setup wizard changes from time to time in new version releases, so maybe small adaptions are needed. This version works for Jira 8.5.0.

Send slack notification

To send a notification to a slack channel a script task is used. The script could look like this.

export JIRANEWESTVERSION=$(echo ${bamboo.inject.jira_newest_version} | tr . -)
slackcli -h <channel name> -u "<username>" -m "New Jira Software version ${bamboo.inject.jira_newest_version} is available! A new Jira image <image-name> was built and can be found in the docker registry. A new container was run from this image. The setup of the instance was performed. You can reach it here: testjira${JIRANEWESTVERSION}.anarcon.net. Use the credentials ${bamboo.JiraUsername}//${bamboo.JiraPassword}" -t "<slack token>"

The slack token has to be generated for a user that is a member of the slack workspace where the channel is located. You can follow these instructions. The username can be chosen, it is simply the name displayed in slack as sender of the message. bamboo-messenger is the name I chose, so it becomes more obvious that the message was automatically sent by Bamboo.

Update variable jira_newest_image_version

As the last step we want the Bamboo agent to “know” that an image/container running the new Jira version exists by updating the variable jira_newest_image_version in the variables.properties file. So the next time the plan is run and no new Jira version is released yet, the variables jira_newest_version and jira_newest_image_version coincide, jira_is_not_up_to_date is evaluated to be False and all conditional tasks building a new Jira image and running a new container are skipped.

This is the python script to update jira_newest_image_version.

#lotte-RenewVariablejira_is_not_up_to_date.py
import os
print("This script updates variables.properties to contain the correct version of the variable jira_newest_image_version after a new Jira image was created")

var_jira_newest_version= os.environ['bamboo_inject_jira_newest_version']

#open file to read variable inside
with open("/var/variables.properties", "r") as f:
    variable_list = f.readlines()

variable_actual = False

#if jira_is_not_up_to_date=False no new image was built in this run of the plan. The jira_newest_image_version does not need to be updated.
for line in variable_list:
    if "jira_is_not_up_to_date=False" in line:
        variable_actual = True
        
print("The jira_newest_image_version variable in the file variables.properties exists and is actual: " + str(variable_actual))

if not variable_actual:
    for i, item in enumerate(variable_list):    
        if "jira_newest_image_version=" in variable_list[i]:
            newline = "jira_newest_image_version=" + var_jira_newest_version + "\n"
            variable_list[i] = newline
    with open("/var/variables.properties", "w") as f:
        f.writelines(variable_list)
            
    print("The jira_newest_image_version variable in the file variables.properties was updated to: " + var_jira_newest_version + ".")

#open file to read variables inside
with open("/var/variables.properties", "r") as f:
    variable_list = f.readlines()
    print("This is the actual list of variables" + str(variable_list))

Failure notifications

To make sure you get notified if the run of a plan fails you can setup email notifications in the Bamboo plan configuration. As positive results will be found in the slack channel, I only setup notifications for failed builds.

We made it!

Gratulations for reading through all of that ;) Even if this setup is a little time consuming, it’s worth it! I definitely learned a lot by combining the functionalities of Docker, Selenium, Bitbucket, Bamboo and Slack. And my colleagues are happy to have test instances of all new Jira versions. By the way, configuring the same for Confluence will feel easy, once you are done with Jira :)