Wednesday, September 16, 2015

DevOps and PowerShell : Automating SonarQube installation - part 1

SonarQube is an open platform to manage code quality. It has been developed with a main objective in mind: make code quality management accessible to everyone with minimal effort. Since version 2.0 SonarQube convers the 7 axes of code quality
  • Architecture and Design
  • Comments
  • Duplications
  • Coding rules
  • Unit tests
  • Potential bugs
  • Complexity

SonarQube embarks a plugin architecture that makes it easy to add rules and matrices by the community and hence contribute to the development. At current state it covers more than 20 programming languages which includes all the popular programming languages currently used for application development. You can find more information on the project and plugins at the SonarQube website.

I was trying to setup a local SonarQube repository on my development machine to test some plugins and features and was looking into the documentation for installing and configuring SonarQube. This involved a series of manual steps to download, install and configure multiple softwares and services and was not fun at all. Well as a big PowerShell and automation fan, I decided to automate the entire process. The following is the summary of my attempt to automate SonarQube installation!!!

Setting up the prerequisites:

SonarQube has the following requirements.

Java runtime (JRE 7 or above)

Database – SQL server in my case with language set to English, Collation to CS and AS.

I created 2 functions to check JRE installed on my machine and install if not present.

function Install-JavaRuntimeEnvironment
{
       [CmdletBinding()]
       param
       (
              [Parameter(Mandatory=$true, Position=0)]
              [ValidateScript({Test-Path $_})]
              [string] $JreInstallerPath
       )

       "Installing java runtime environment" | Write-Verbose

       $arguments = "/s SPONSORS=0 /L $Env:Temp\jre_install.log"

       $proc = Start-Process $JreInstallerPath -ArgumentList $arguments -Wait -NoNewWindow -PassThru
       if($proc.ExitCode -ne 0)
       {
              throw "Unexpected error installing java runtime environment"
       }
       [Environment]::SetEnvironmentVariable('JAVA_HOME', "C:\Program Files\Java\jre1.8.0_60\bin", "Machine")
}

function Test-JavaInstalled
{
       $javaPackage = Get-InstalledSoftwares |? {$_.DisplayName.Contains('Java 8 Update 60')}
       return $javaPackage -ne $null
}

Which I can call from my main script as

#Install JRE
if(-not (Test-JavaInstalled))
{
       Install-JavaRuntimeEnvironment "\\myshare\softwares\java\jre-8u60-windows-x64.exe" -Verbose
}

The next step was to setup the SQL Server properly for the installation. To make sure that the language and collation are set properly I created couple of functions again in my modules.

function Get-SqlServerProperty
{
       [CmdletBinding()]
       param
       (
              [Parameter(Mandatory=$false)]
              [string] $ServerName = "localhost",

              [Parameter(Mandatory=$true, Position=0)]
              [string] $ServerProperty
       )

       $connection = New-Object System.Data.SqlClient.SqlConnection
       $command = New-Object System.Data.SqlClient.SqlCommand

       try
       {
              $connection.ConnectionString = "Server=$Server;Database=master;Integrated Security=True"
              $connection.Open()

              $command.Connection = $connection
              $command.CommandText = "SELECT SERVERPROPERTY ('$ServerProperty')"

              $command.ExecuteScalar()
              $connection.Close()
       }
       finally
       {
              $connection.Dispose();
       }
}

function Test-SqlServerProperties
{
       [CmdletBinding()]
       param
       (
              [Parameter(Mandatory=$false, Position=0)]
              [string] $ServerName = "localhost"
       )

       $collation = Get-SqlServerProperty "Collation" -ServerName $ServerName
       "SQL server collation : $collation" | Write-Verbose

       $lcid = Get-SqlServerProperty "LCID" -ServerName $ServerName
       "SQL server LCID : $lcid" | Write-Verbose

       ($collation -eq "LATIN1_General_CI_AS") -and ($lcid -eq 1033)
}

These methods are used to check whether the SQL server is in the state we can use to have the SonarQube databases created. Else we throw an error and stop the installation.

#Validate SQL server prerequisites
if(-not(Test-SqlServerProperties $SqlServer -Verbose:$RunAsVerboseSession))
{
       "Unable to validate SQL Server properties on the server : $SqlServer" | Write-Warning
       "You need to ensure that the database requirements match the SonarQube specifications" | Write-Warning
}

After working on the prerequisites, we  now need to download SonarQube from the website. SonarQube package is distributed as a zip file which can be download from the URL https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-5.1.2.zip.

function Get-SonarQube
{
       [CmdletBinding()]
       param
       (
              [Parameter(Mandatory=$false, Position=0)]
              [string] $DownloadLink = "https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-5.1.2.zip",

              [Parameter(Mandatory=$false, Position=1)]
              [ValidateScript({Test-Path $_})]
              [string] $DownloadLocation = "C:\Windows\Temp"
       )

       $fileName = $DownloadLink.SubString($DownloadLink.LastIndexOf("/") + 1)

       $target = Join-Path $DownloadLocation $fileName
      
       "Starting download file $DownloadLink" | Write-Verbose
       Invoke-WebRequest -Uri $DownloadLink -OutFile $target
       "Completed download to $target" | Write-Verbose
       $target
}

function Expand-SonarQubePackage
{
       [CmdletBinding()]
       param
       (
              [Parameter(Mandatory=$false, Position=0)]
              [ValidateScript({Test-Path $_})]
              [string] $Source = "C:\Windows\Temp\sonarqube-5.1.2.zip",

              [Parameter(Mandatory=$false, Position=1)]
              [string] $Target = "C:\SonarQube"
       )

       if(-not(Test-Path $Target))
       {
              "Creating new folder at location : $Target" | Write-Verbose
              New-Item -ItemType Directory -Path $Target -Force | Out-Null
       }

       Add-Type -AssemblyName "System.IO.Compression.FileSystem" |  Out-Null

       "Extracting the contents of $Source to $Target" | Write-Verbose
       [IO.Compression.ZipFile]::ExtractToDirectory($Source, $Target)
}

The above methods will help us download the package from the website and extract the contents to a local folder on the machine.

$installFolder = "C:\SonarQube"

$source = " C:\Windows\Temp\sonarqube-5.1.2.zip "
#Download SonarQube server files
if(-not(Test-Path $source))
{
       Get-SonarQube -DownloadLocation (Split-Path -Parent $source) -Verbose:$RunAsVerboseSession
}

#Extract SonarQube files to the installation folder
$sonarQubeFolder = Join-Path $installFolder ([IO.Path]::GetFileNameWithoutExtension((Split-Path -Leaf $source)))
if(-not(Test-Path $sonarQubeFolder))
{
       Expand-SonarQubePackage -Source $source -Target $installFolder -Verbose:$RunAsVerboseSession
}


In the next post of this series we’ll look into how to configure SQL server for our use by adding a login and database for SonarQube.

No comments: