In looking around the web, I don’t see much on how to get SonarQube working in AWS. This blog will cover how to setup SonarQube in AWS for simple code analytics.

I.        Background

SonarQube is a nice open source code analysis server that you host yourself. Code analysis and central reporting – measuring complexity, dependencies, coverage, etc.  – is great. If you are in a Microsoft stack you can use TFS to get these numbers, but for open source, across multiple languages, tools are more limited and SonarQube is still one of the better ones.

You can host SonarQube on your own machines and show the reports from there, but if you are doing everything in the cloud or have a distributed team, it’s a little trickier. This blog will cover that case.

II.      Goals and Requirements

  • I need code analysis that shows code complexity, common bugs, and supports multiple languages like Java and NodeJS.
  • I need it to work with many repos and users. There are tools that charge by repo or user, but costs would quickly escalate for those.
  • I want to use free services as much as possible. My code is not open source, so I can’t take advantage of a lot of the services that are free to open source.
  • I’m using GitHub private repositories and CircleCI for continuous build and integration.

These are my goals. You may not need all of them, but if you want to make code quality part of your build and deployment process SonarQube in AWS is a reasonable way to go.

Procedure

I. Get SonarQube running with its built-in database

Create your AWS instance

I went with the simple Amazon medium instance Linux 64 bit. The docs say you might need up to a Large – it will depend on how many users you have. 2GB of RAM is the minimum.

Linux + SonarQube doesn’t have a large disk footprint, a little over a Gig, so the volume doesn’t need to be large. Include room for a copy of your code. I started with an 8G volume, which was overkill.

Open up a custom port tcp:9000 for access to the SonarQube site. That and your ssh:20 port are all you’ll need.

  • Download and unzip SonarQube and the SonarQube Scanner. SonarQube installation is here. Scanner installation is here

In my case I just downloaded and unzipped the files on my Windows desktop then copied them to the AWS machine using WinSCP. SonarQube suggests putting the server in /etc, which may require an extra sudo cp step.

  • Upgrade Java in AWS to one that works with SonarQube

Install Java

sudo yum install java-1.8.0
change the Java version
sudo alternatives --config java
  • Start the service and test it
cd /etc/sonarqube-5.6.3/bin/linux-x86-64
sudo ./sonar.sh start

With a browser connect to http://<my_aws_ip_or_dns_name>:9000 to verify that SonarQube is running.

II.      Switch SonarQube to a Postgres backend

SonarQube shouldn’t be run in production with the default DB it comes with, so let’s now move it to a Postgres DB. You can use anything, but a local Postgres instance is free.

Ideally, you’d put your Postgres data on a separate volume for easy management, but because I don’t care about the historical data and will just recreate the DB if I need to restore it, I left the DB on the main volume

To install Postgres in AWS you could follow this guide. It recommends that you create the extra volume for the Postgres data if you want. That’s a good practice, in general, but since this data can be rebuilt with a scan, I didn’t bother and just did a quick and dirty local installation.

For a quick Postgres installation just do this:

sudo yum install postgresql9-server

Set the Postgres authentication and user/password

sudo -u postgres psql postgres # set the default password for Postgres

now also  (still as the postgres account, sudo su – postgres if you’re not, exit later)

cd /var/lib/pgsql9/data

edit pg_hba.conf  to change peer to md5 as per these instructions

Create the Empty DB and User

From the docs: Create an empty schema and a SonarQube user. Grant this SonarQube user permissions to create, update and delete objects for this schema. The charset of the database has to be set to “UTF-8” and the language (database and user) to “English”.

su – postgres
psql
CREATE USER myuser WITH PASSWORD 'myPassword';
CREATE DATABASE sonarqube ;
GRANT ALL PRIVILEGES ON DATABASE sonarqube to myuser;
quit

Startup the Postgress service

sudo service postgresql initdb #this is a one-time operation
sudo service postgresql start

Configure SonarQube to use Postgres

Edit the /conf/sonar.properties file as described in the SonarQube installation docs.

Now restart SonarQube

cd /etc/sonarqube-5.6.3/bin/linux-x86-64
sudo ./sonar.sh stop
sudo ./sonar.sh start

and test again in the browser

III.    Add some minimal security

Hey, this is your source code. You probably don’t want to expose it on an http connection unless you are open source.

  • Login as the SonarCube admin and change the admin password
  • Create a user account to scan code (you’ll use it below)
  • Create other user accounts for your users

IV.   Add your code

I found the easiest way to setup GitHub was just to copy my local GitHub repo over to the AWS server using WinSCP.

try a git pull on the AWS server to see if you can sync the code

Setup the sonar-project.properties files to scan the code following the guidance here. You could have one sonar-project.properties file for all the repos or a separate file for each. I went with a separate file for each ‘cause that way I can easily run a scan on just one repo and later I can create the on-demand service for a single repo.

In the sonar-project.properties file for each repo add:

sonar.login=theScanAccount
sonar.password=thePassword

so the scan process can connect to SonarQube

You’ll also have to edit the standard metadata. Example:

# Required metadata
sonar.projectKey=myproject
sonar.projectName=myproject
sonar.projectVersion=1.0

# Comma-separated paths to directories with sources (required)
sonar.sources=src

# Language
sonar.language=js

Now try a scan using

/etc/sonar-scanner-2.8/bin/sonar-scanner

I put the scanner, in /etc, you might have put it in /usr or /home adjust the path accordingly.

Now check out the SonarQube web UI to see your data. This is probably a good time to configure your rules. The code quality rules that SonarQube starts with may not match your code style guidelines, so it’s good to get those in sync before you show this to your team.

V.     Adding Code Coverage to SonarQube

If you have SonarQube in AWS, you probably run your tests there or in some cloud testing tool.  I’m using CircleCI for the build so the test and coverage files are on their servers.

Run aws config to add the key, secret key and region to your local config files. Region should be the full name such as “us-west-1”.

In my case, I’m using CircleCI, so at the end of my build I have a code coverage file, lcov.info, that’s on the CircleICI server, that I need to get to my SonarQube server in AWS. To do this, I copy the coverage file to an AWS bucket, and use a script to copy in from the bucket when I run the SonarQube scan.

In the CircleCI circle.yml

test:
override:
[run the tests and generate the lcov.info file, then]
aws s3 cp ./coverage/lcov.info s3://your-aws-bucket/sonar/your-repo/

Automating

I’m going to tell you how to set this up for a scheduled build, rather than an on-demand continuous build. You can do an on-demand build, but it requires a very small rest service on the server (and another open port). I’m talk about that later.

I.        Create an automation script

Create a script to automate the scans. I called mine pullAll.sh. Here’s some code to do a git pull each repo, copy the coverage file from an amazon bucket, and finally run the SonarQube scan.

function pullRepo {
echo " "
echo "entering repo "$1
cd ../$1
pwd
git checkout master
git pull

aws s3 sync s3://your-aws-bucket-here/sonar/$1/ .

if [ -f "lcov.info" ]; then #copy over the coverage data
sed 's/home\/ubuntu/data/g' lcov.info > lcov.info.txt
mv -f lcov.info.txt lcov.info
fi

if [ -f "sonar-project.properties" ]; then #scan
/etc/sonar-scanner-2.8/bin/sonar-scanner
else
echo "skipping scan for " $1
fi
}

cd /data/your-repo

pullRepo your-repo
pullRepo next-next-repo

#etc.

Sure, I could’ve written some fancy script that went into every directory, but this is simple to maintain and easy to control if you want to just scan some repos.

II.      Schedule the scans

To update the scan on a regular basis, create a simple sh script in /etc/cron.daily (or whatever frequency you want). It should contain something like this:

/usr/bin/pullAll.sh > /home/ec2-user/pullAll.log

III.    Implementing on-demand build

I didn’t do this part, so I’ve left it as an exercise to the reader, if you do it, send me the code!

All you need is a small restful web service that runs on the SonarQube machine. If you call a single API “scanRepo” with a parameter of the repo name it will run the script to pull and scan that repo. It’s probably about 10 lines in NodeJS.