5/01/2015

Jenkins, S3 Copy Artifact, Deploy Plugin, and ROOT Context Tricks

I've spent several frustrating days trying to get Jenkins to deploy my application remotely to my Tomcat 7.x app server. My build setup is pretty vanilla Java application built with Ant and Ivy. Overall I like Jenkins and I'm trying to learn how to use it for continuous deployment, but the lack of documentation explaining some of the plugins makes it extremely frustrating. This hopefully will explain some of the subtle configuration options better for these plugins. For this I've setup Jenkins with these plugins:

I have two jobs. One builds my application which uses a post build step to Publish artifacts to S3 bucket. The second job is used to remotely deploy the artifacts from the first job to the Tomcat 7.x server.

The 2nd job is a parameterized build with the following configuration:

Build selector for Copy Artifact
name = BUILD_SELECTOR

Execute Shell
Command = rm -rf $WORKSPACE/build

Copy S3 Artifact
Project Name = MyApp
Which Build = Specified by Build Parameter
Parameter Name = BUILD_SELECTOR
Artifact Copy = webappname-*.war
Target Directory = $WORKSPACE/build

Few things to note. BUILD_SELECTOR is the name of the environment variable that holds the user's selected build. The artifact to copy setting is not a path it's just a pattern used to select the artifact. I execute the rm command to clean up the artifacts between successive builds.

The first problem I encountered was the 2nd job kept failing because it said there were not any artifacts from the 1st job. It is NOT documented anywhere that I could find, but once I went back to the 1st Job and marked the "Publish artifacts to S3 bucket" step as "Manage Artifacts". Once that was checked it finally recognized the artifacts and I got the following!

Copied 1 artifact from "MyApp" build number 301 stored in S3

But the next problem was the deploy plugin kept failing with a very obtuse error.

java.io.IOException: Error writing request body to server

I found out that if I removed my application from the Tomcat 7.x webapps directory then it would actually deploy! But if I tried to redeploy it it failed with that obtuse error. I was deploying my app to Tomcat's ROOT context so my configuration looked like this:

WAR/EAR = build/fuseanalytics-*.war
Context = ROOT
Container = Tomcat7x
Manager User Name = none of your business
Password = also none of your business
Tomcat URL = http://somehost

So the clue was the following logging written out in the console output: "is not deployed."

Copied 1 artifact from "MyApp" build number 301 stored in S3
Deploying /var/lib/jenkins/workspace/MyApp/build/myapp-1.0-301.war to container Tomcat 7.x Remote
  [/var/lib/jenkins/workspace/DeployMyApp/build/myapp-1.0-301.war] is not deployed. Doing a fresh deployment.

So after looking through the Cargo code (what the deploy plugin is based on) I found out that Cargo has to use redeploy or undeploy if a webapp is already deployed. So why is it not working? It turns out Cargo cannot handle the Context of ROOT. The tomcat manager data doesn't specify the webapp deployed on ROOT! The work around is to change ROOT to '/' (without quotes) in Jenkins and viola! It works!