Intergrate sonarqube in Jenkins. A gentle intro to Jenkins DevSecOps.

In this article, you will learn how to set up Jenkins in a digital ocean droplet, configure it, Install sonarqube, configure and link it to our Jenkins pipeline and how to send emails in Jenkins. We will also finally deploy the application to an aws ec2 instance.

This article assumes a basic understanding of docker.

1. Set up, configure and install Jenkins on a digital ocean droplet.

1.1 Head over to Digital Ocean and create an account if you do not already have one, otherwise log in.
1.2 Create a digital ocean droplet with the following specs.
  • An image - Ubuntu: the Lts tag
  • Plan - 4GB 2 AMD CPUs
  • Datacenter region - (preferably one close to your location)
  • Authentication method - (ssh keys) and copy your public key to Digital Ocean. follow this link to create ssh keys.
1.3 Configure the firewall rules to open port 22 (for ssh connections ) and port 8080 (for our Jenkins server).

Screenshot from 2022-09-25 12-04-55.png

Ensure ports 22 and 8080 are open to receive requests.
1.4 Install docker in the droplet.

On your machine, ssh into the digital ocean droplet by

ssh YOUR_DROPLET_IP@root

Update the digital ocean droplet by

apt update

Upgrade the digital ocean dependencies by:

apt upgrade -y

Install docker runtime engine by:

apt install docker.io -y
1.5 Run Jenkins as a docker container

Run the following command whilst being ssh'ed into the digital ocean droplet.

docker run -p 8080:8080 -p 50000:50000 -d \ 
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(which docker):/usr/bin/docker jenkins/jenkins:lts

Access the Jenkins server on the web using the server's public IP as shown below

YOUR_DO_DROPLET_IP:8080

After a successful installation of Jenkins in the Docker container, you should see a screen similar to the one below that will prompt you for an administrator password

#use this command to get the initialAdminPassword
cat /var/lib/docker/volumes/jenkins_home/_data/secrets/initialAdminPassword

copy the password and login into Jenkins. We can now create our first Jenkins user.

Screenshot from 2022-09-25 12-23-50.png

2. Install and configure sonarqube in the digital ocean droplet.

2.1 SSH into the digital ocean droplet by
ssh YOUR_DO_IP@root
2.2 Run sonarqube as a docker container
docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube

N/B ensure that port 9000 is configured on your firewall and can receive requests

Login to the sonarqube server by using the default username admin and default password admin

You will be redirected to a page where you will reset the default password

Screenshot from 2022-09-25 13-04-20.png

You will now be redirected to the sonarqube dashboard

Screenshot from 2022-09-25 13-07-05.png

You have successfully installed sonarqube and Jenkins on the digital ocean droplet

2.3 Configure Sonarqube in Jenkins.

In your Jenkins dashboard, navigate to the manage Jenkins -> Manage Jenkins and install the Sonarqube scanner

Screenshot from 2022-09-25 22-30-14.png

After a successful installation, head over to the manage Jenkins - configure systems and head over to the Sonarqube Servers part and configure it as shown in the pic below.

Screenshot from 2022-09-25 23-18-49.png

Kindly ensure that you have created a token in Sonarqube by going to the My Account -> Security part and have added the token in Jenkins as a credential.

3. Create a multi-branch pipeline and configure it for Django.

Head over to your Jenkins Dashboard and click on the New Icon tab. You will be redirected to this page, input the job name and select the multi-branch job option.

Screenshot from 2022-10-06 21-54-38.png

On successful creation, head over to the configure section -> branch sources and set the source as GitHub. Now add in your GitHub credentials previous added and the remote branch.

Screenshot from 2022-10-06 21-59-34.png

You can now head over to your project and add the following code in the JenkinsFile.

#! env/bin/groovy 
def version 
// def newVersion
pipeline {
    agent any 
    stages { 
        stage("Init the application") {
            steps { 
                script {
                    echo "Hello there, ${BRANCH_NAME}"
                }
            }
        }

        stage("Create a virtual enviroment") { 
            steps{
                script{
                    echo "Now creating a virtualenv"
                    sh "python3 -m virtualenv venv"
                    sh "chmod +x venv/"
                    sh "ls"
                    sh ". venv/bin/activate"
                }
            }
        }
        stage("Read the current version of the application") { 
            steps { 
                script { 
                    echo" build number ${BUILD_NUMBER}"
                    def matcher = readFile("__init__.py") =~  "version = (.+)"
                    echo "${matcher[0][1]}"
                    version = matcher[0][1]
                }
            }
        }
        stage("Test stage") { 
            steps { 
                script{
                    echo "now what"
                    // sh "pytest"
                }
            }
        }
        stage("Build") { 
            steps { 
                script{
                    echo "now what"

                    withCredentials([usernamePassword(credentialsId:'dockerhub-id', usernameVariable:"USER" , passwordVariable:"PASS")]) { 
                        // sh " echo ${PASS} | docker login -u $USER --stdin-"
                        sh ''' 
                    docker system prune -a -f 
                    docker-compose -f docker-compose.yaml down
                    docker-compose -f docker-compose.yaml up --build   -d
                    echo $PASS | docker login -u $USER --password-stdin
                    docker push manulangat/django-ecomerce-monolith
                    '''

                    }

                }
            }
        }

        stage('Commit the version and tag'){
            steps {
                script {

                    echo" build number ${BUILD_NUMBER}"
                    def matchers = readFile("__init__.py") =~  "version = (.+)"
                    echo "${matchers[0][1]}"

                    def vers = matchers[0][1]
                    echo "${vers}"
                    matchers = null
                    // def newVersion = matchers[0][1]
                    // echo "${newVersion}"
                    // sh "docker-compose -f docker-compose.yaml down"
// now
                    withCredentials([usernamePassword(credentialsId:"github-creds-hook", usernameVariable:'USER', passwordVariable:'PASS')]) { 
                        sh '''
                        chmod +x venv/
                        . venv/bin/activate
                        pip install -r requirements.txt

                        git config --global user.name jenkins
                        git config --global user.email jenkins@jobs.com
                        git status
                        git config --list
                        git remote set-url origin https://$USER:$PASS@github.com/manulangat1/ecomerce-monolith.git
                        git push --tags -f
                        git tag | xargs git tag -d
                        chmod +x README.md 
                        chmod +x __init__.py 
                        chmod +x setup.py 
                        chmod +x CHANGELOG.md
                        bump2version  minor --allow-dirty
                        git describe --always
                        git push --tags -f
                        git tag | xargs git tag -d


                        '''
                        echo "Done pushing to github"
                    }
                }
            }
        }
    }

}

You can now connect the sonarqube server to the Jenkins server by going to the Jenkins dashboard and configure systems and head over to the Sonarqube installations part and fill in the required data.

Screenshot from 2022-10-06 22-17-51.png

You can now add the stage to run sast analysis using sonarqube

stage('SonarQube Analysis') {
                steps {
                    script {
                        def scannerHome = tool 'SonarScanner';
                            withSonarQubeEnv() {
                            sh "${scannerHome}/bin/sonar-scanner"
                            }
                    }
                }
            }

At this stage, your JenkinsFile should resemble the one below.

#! env/bin/groovy 
def version 
// def newVersion
pipeline {
    agent any 
    stages { 
        stage("Init the application") {
            steps { 
                script {
                    echo "Hello there, ${BRANCH_NAME}"
                }
            }
        }
        stage('SonarQube Analysis') {
                steps {
                    script {
                        def scannerHome = tool 'SonarScanner';
                            withSonarQubeEnv() {
                            sh "${scannerHome}/bin/sonar-scanner"
                            }
                    }
                }
            }
        stage("Create a virtual enviroment") { 
            steps{
                script{
                    echo "Now creating a virtualenv"
                    sh "python3 -m virtualenv venv"
                    sh "chmod +x venv/"
                    sh "ls"
                    sh ". venv/bin/activate"
                }
            }
        }
        stage("Read the current version of the application") { 
            steps { 
                script { 
                    echo" build number ${BUILD_NUMBER}"
                    def matcher = readFile("__init__.py") =~  "version = (.+)"
                    echo "${matcher[0][1]}"
                    version = matcher[0][1]
                }
            }
        }
        stage("Test stage") { 
            steps { 
                script{
                    echo "now what"
                    // sh "pytest"
                }
            }
        }
        stage("Build") { 
            steps { 
                script{
                    echo "now what"

                    withCredentials([usernamePassword(credentialsId:'dockerhub-id', usernameVariable:"USER" , passwordVariable:"PASS")]) { 
                        // sh " echo ${PASS} | docker login -u $USER --stdin-"
                        sh ''' 
                    docker system prune -a -f 
                    docker-compose -f docker-compose.yaml down
                    docker-compose -f docker-compose.yaml up --build   -d
                    echo $PASS | docker login -u $USER --password-stdin
                    docker push manulangat/django-ecomerce-monolith
                    '''

                    }

                }
            }
        }

        stage('Commit the version and tag'){
            steps {
                script {

                    echo" build number ${BUILD_NUMBER}"
                    def matchers = readFile("__init__.py") =~  "version = (.+)"
                    echo "${matchers[0][1]}"

                    def vers = matchers[0][1]
                    echo "${vers}"
                    matchers = null
                    // def newVersion = matchers[0][1]
                    // echo "${newVersion}"
                    // sh "docker-compose -f docker-compose.yaml down"
// now
                    withCredentials([usernamePassword(credentialsId:"github-creds-hook", usernameVariable:'USER', passwordVariable:'PASS')]) { 
                        sh '''
                        chmod +x venv/
                        . venv/bin/activate
                        pip install -r requirements.txt

                        git config --global user.name jenkins
                        git config --global user.email jenkins@jobs.com
                        git status
                        git config --list
                        git remote set-url origin https://$USER:$PASS@github.com/manulangat1/ecomerce-monolith.git
                        git push --tags -f
                        git tag | xargs git tag -d
                        chmod +x README.md 
                        chmod +x __init__.py 
                        chmod +x setup.py 
                        chmod +x CHANGELOG.md
                        bump2version  minor --allow-dirty
                        git describe --always
                        git push --tags -f
                        git tag | xargs git tag -d


                        '''
                        echo "Done pushing to github"
                    }
                }
            }
        }
    }

}

Now push your code to GitHub and head over to your Jenkins Dashboard and navigate to the multi-branch job created earlier and click on the Build now button to start a new build. Your pipeline should run as expected

Navigate to your sonarqube server and on the dashboard, you should see your latest build as shown in the image below. You can go through it to understand how your code compares against several metrics.

Screenshot from 2022-10-06 23-18-02.png

4. Intergrate Email sending functionality.

Email integrations into the pipeline will help in sending alerts when specific actions occur. This can be achieved by:

4.1 Install the email extension library

Head over to your Jenkins dashboard -> manage plugins section and install the Email Extension plugin

4.2 Configuring email extension Library

After successful installation, go to the configure system and scroll down to the extended email part and set up your SMTP servers.

Screenshot from 2022-10-06 23-40-06.png

Once that is done, head to your JenkinsFile and add the following block after all the stages.

    post{ 
    always { 
        sh '''
        docker system prune -a -f 
        docker-compose -f docker-compose.yaml down
        rm -rf venv
        '''
        emailext body: "$JOB_NAME - Build # $BUILD_NUMBER - :Check console output at $BUILD_URL to view the results.", recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']], subject: 'Test'
    }
    failure { 
        echo "oops sth failed"
    }
}

This step shuts down our docker containers and sends an email about the build. Commit and push your code to GitHub and head over to Jenkins to trigger a build. You should receive an email about the build details.

In this article, we have gone through setting up Jenkins, Sonarqube and email integration and how they work together in a pipeline. In the next article, we shall look at how to set up slack notifications in the pipeline and how to integrate dast (dynamic application software testing) in the pipeline.

Happy hacking