Tuesday, October 15, 2013

Cloudfoundry makes continuous integration easy

Cloudfoundry works as a PAAS that makes dev opts work much easier for continuous integration.

Without a good PAAS, setting up a stable and reusable build environment requires a lot of efforts. For example, the build requires a tomcat container setup, the Jenkin slave machine needs to avoid the tomcat port conflict, the web application has other service dependencies for testing, needs profiles to separate the different environments and injecting to your pom.xml and so on.

I will describe an experiment of using cloudfoundry and maven to do a clean integration test.

Prerequisite:

  • Register a cloudfoundry account https://console.run.pivotal.io/register with a trial account or you can create a light weight cloudfoundry by using https://github.com/cloudfoundry/bosh-lite (Recommended since you can get better understanding cloudfoundry principles and components)

Setup the POM (example as https://github.com/datianshi/spring-mvc-service with cloudfoundry branch):
  • Add cloudfoundry credentials to your maven settings.xml
<servers>
        <server>
          <id>mycloudfoundry-instance</id>
          <username>${username}</username>
          <password>${password}<password>
        </server>
</servers>


  • Add cloudfoundry maven plugin (cf-maven-plugin) for pre-integration-test and post-integration-test
<plugin>
	<groupId>org.cloudfoundry</groupId>
	<artifactId>cf-maven-plugin</artifactId>
	<version>1.0.0.BUILD-SNAPSHOT</version>
	<configuration>
                    <server>mycloudfoundry-instance</server>
                    <target>http://10.244.0.14:9022</target>
                    <org>myorg</org>
                    <space>myspace</space>
                    <appname>${app}</appname>
                    <url>${app}.10.244.0.254.xip.io</url>
                    <memory>256</memory>
                    <buildpack>git://github.com/datianshi/java-buildpack.git</buildpack>
	</configuration>
	<executions>
		    <execution>
			  <id>pre-integration</id>
			  <phase>pre-integration-test</phase>
			  <goals>
			  	<goal>push</goal>
			  </goals>
			  </execution>
		    <execution>
			  <id>post-integration</id>
			  <phase>post-integration-test</phase>
			  <goals>
			  	<goal>delete</goal>
			  </goals>
	            </execution>			  	
	</executions>
</plugin>

The above plugin has two executions, one is set as pre-integration-test life cycle with a push goal to set up a tomcat environment. The application can be configured with appname, url and so on. What does it actually happen during push inside of cloud foundry? In short story, the cf client side upload the war and give that to the cloudfoundry (specified in the target url), and cloudfoundry is smart enough to create a ready tomcat container based on the build packs, url(it has internal DNS server) and app name. For the interest readers, please refer cloudfoundry architecture and how-applications-are-staged




  • Add maven fail safe plugin to run your integration tests.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
        <skip>true</skip>
</configuration>
<executions>
        <execution>
                <id>integration-tests</id>
                <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                </goals>
                <configuration>
                        <skip>false</skip>
                        <includes>
                                <include>**/*IntegrationTest.java</include>
                        </includes>
                        <systemPropertyVariables>
                                <integrationUrl>http://${app}.10.244.0.254.xip.io</integrationUrl>
                        </systemPropertyVariables>
                </configuration>
        </execution>
</executions>
</plugin>

Since we parameterized the app name, we could just pass in the url to our test and the test knows which url to perform test. Because of the existence of router and dns server in cloudfoundry, we would not worry port configuration/collision for tomcat, since every jenkin build we just pass in different app name (E.g. build number), then the build executes concurrently.

To run the build: mvn clean install -Dapp=test

Cloudfoundry is also flexible to integrate with other application containers (Controlled by buildpack). Another feature called services (As your application dependencies E.g. mysql, redis....) could be configured and deployed to cloudfoundry environment so your application could be easily bind to during push with minimum configuration. For the readers interested with cloudfoundry deployment and release management, please refer bosh