Continuous integration for Spring Boot app using Codeship + Openshift

I have already talked about CI using pair Codeship + Openshift and I like such combination. Recently, I have started a new project based on spring boot and faced an issue, that Openshift does not provide an out-of-box pattern to run Spring Boot application.

Spring boot application could be built in two ways: with embedded tomcat and without it. After reading some tutorials, how to build an application without tomcat, I decided not to go this way - it requires some code changes + some kind of settings tuning on Openshift side. The solution with embedded tomcat looks more consistent and reliable in this case for me.

To setup spring boot app you need DIY gear with maven and Java 8. I was lucky and found a repo on github with sample spring boot application on DYI openshift gear. So I use this code as the start of my application. The main magic are located in file .openshift/action_hooks/deploy. It downloads Maven and setups all needed environment variables.

As next step, you should build and test your Spring Boot project on Codeship. All technical details about setup Codeship can be found here. I will notice that in this tutorial the build was created for Grails project, so test and deploy steps for Spring Boot project will be slightly different.

First of all, we need to use Java 8 on codeship server for tests instead of default Java 7. To enable Java 8 in setup section of Project settings -> Test include this:

jdk_switcher home oraclejdk8
jdk_switcher use oraclejdk8

Test script has nothing special except Spring profile for CI execution:

mvn clean initialize
mvn clean test -Dspring.profiles.active=ci
mvn package -Dmaven.test.skip=true

Deployment script is the most interesting part here. Usually, when you use standard Tomcat Openshift gear, all you need is place WAR file in a correct directory and it will be automatically deployed. I want to use embedded tomcat, so I get packed JAR which should be started manually. Such case was not what I desire (take into account that before starting I should stop old running version of the application). Performing all these operations from deployment script located on Codeship was also not possible due to insufficient access rights to start and stop processes. All I can do remotely - file operations and controlling Openshift gear lifecycle. It gives me a way to resolve deployment problem:

  1. Stop running gear.
  2. Delete old JAR.
  3. Copy freshly built JAR from Codeship to Openshift.
  4. Start gear.
  5. Openshift action hook that triggers by a start of gear will start the application from new JAR.

The first 4 steps of plan are located in Codeship deploy script:

ssh <some_hash>@project_name-acc_name.rhcloud.com 'gear stop'
scp -rp /home/rof/src/<codeship_account>/<codeship_project>/target/*.jar <some_hash>@project_name-acc_name.rhcloud.com:~/app-root/runtime/repo/target/
ssh <some_hash>@project_name-acc_name.rhcloud.com 'gear start'
wget --retry-connrefused --no-check-certificate -T 60 https://project_name-acc_name.rhcloud.com/somepath/to/ping

Actually, there is no need to remove an old JAR, because it always has the same name, so the new file will overwrite the old one. I add the ping action as the last command to be sure the application is up and running once I see that build is passed.

Now let's take a look at the 5th step of the plan - starting the application by action hook. All Openshift hook are placed at .openshift/action_hooks in git repository of your Openshift project. As I started with the sample of Spring boot layout for Openshift I already have a script for action hook for gear starting: .openshift/action_hooks/start. The main part of this script is this line:

nohup java -Xms384m -Xmx412m -jar target/*.jar --server.address=${OPENSHIFT_DIY_IP} --spring.profiles.active=openshift &

So all I need here is to be sure that I have correct path and Spring profile.