Blog‎ > ‎

CloudBees, Ant and SCP

We use Cloudbees for running builds, tests, etc... It's overall very good, but it can sometimes be difficult to debug. I just ran into such a problem, and I thought I'd put it out there in case anyone else encounters it.

My challenge seemed simple: I wanted to have an Ant target that would deploy a bunch of files to an EC2 instance. How hard can that be?

Not at all obvious, as it turns out. There are several things that need to go right to get this to work.

1 - Create a new key

Obviously, we assume that you can ssh into your EC2 instance. If you can do that, then clearly you're using a public/private key pair, so you might be tempted to use that. It would generally be considered a bad idea because, if that key becomes compromised, it might be a hassle to revoke it and get a new key.

So, generally speaking, we prefer a key that's specifically for the purpose of the build, and can be revoked with minimal consequences. 

Similarly, you should normally not use the ec2-user user to transfer files to an EC2 instance. This is a highly privileged user, and it's best to use the lowest possible level of privilege to do the job. So I'd recommend you create a special user on your EC2 instance, and give it only those privileges that are required to do whatever you need. I'll assume that this special user is named buildUser from now on.


Now let's create a new key:

MyMac$ ssh-keygen -t rsa -C "buildUser"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/jdoe/.ssh/id_rsa): ./CloudbeesToEC2
Enter passphrase (empty for no passphrase): secret
Enter same passphrase again: secret
Your identification has been saved in ./CloudbeesToEC2.
Your public key has been saved in ./CloudbeesToEC2.pub.

Obviously the location and name of the file is up to you, and your passphrase should be more secure than this!
This will create two files: CloudbeesToEC2, which is your private key, and CloudbeesToEC2.pub, which is your public key.

Take the contents of CloudbeesToEC2.pub and add it to the /home/buildUser/.ssh/authorized_keys file on your EC2 instance. That's all you need to do on the EC2 instance.


2 - Copy your private key file to CloudBees

The next step is to make the private key file available to the build script. The obvious way to do that would be to check it into source control, but that would be a bad idea. Files in source control get replicated all over the place, and we don't want our private key floating around.

A better way is to copy the private key file (CloudbeesToEC2 in our example) to your Cloudbees private repository. This private repository is accessible through WebDAV, so on a Mac, you can just use the Finder. Select Go -> Connect to Server...

https://repository-{account-id}.forge.cloudbees.com/private/ 

I created a directory called Keys and copied the private key file to that directory.


3 - Set up your Ant script

Having set this up, we can now define an Ant script to do the file copying:

<target name="publishSiteFromCloudBeesToEC2">
  <scp todir="buildUser@ec2-12-34-56-78.compute-1.amazonaws.com:/path/to/copy/files/to" 
          trust="true" keyfile="/private/abl/Keys/CloudbeesToEC2">
    <fileset dir="../WebContent">
      <include name="**/*.html"/
      <include name="**/*.css"/>
    </fileset>
  </scp>
</target>

We're not done though. This script won't work from CloudBees yet, because we need to do a couple more things.


4 - Make the Jsch library available to CloudBees

If you were to run your Ant script on CloudBees right now, you'd most likely get an error like:

BUILD FAILED
/scratch/jenkins/workspace/Publish site/KahunaSite/scripts/build.xml:22: Problem: failed to create task or type scp
Cause: Could not load a dependent class com/jcraft/jsch/Logger
       It is not enough to have Ant's optional JARs
       you need the JAR files that the optional tasks depend upon.
       Ant's optional task dependencies are listed in the manual.
Action: Determine what extra JAR files are needed, and place them in one of:
        -/opt/ant/latest/lib
        -/home/jenkins/.ant/lib
        -a directory added on the command line with the -lib argument


This is because we need to make the Jsch jar available to Ant. To do so, you need to copy
the required jar to your private repository. I chose to copy the file to a directory structure that's compatible with Maven, but that's up to you. It just needs to be somewhere in the private repository.

Just like for the private key file, you can use the Finder on a Mac (or any WebDAV client) to copy the jar file over.

5 - Invoke your Ant script with the Jsch jar available

The last step is to make that jar file available to Ant. You can do that by adding a -lib option to your targets, pointing to the directory that contains the jar file for Jsch. Note that this is the directory that contains the jar, and not the jar itself.


Note that the Targets field contains the -lib option. The name of the target comes last.

That should be it. If your build runs successfully, you should see something like:

At revision 17
[scripts] $ ant -file build.xml "-Dworkspace=/scratch/jenkins/workspace/Publish site" -lib /private/abl/com.jcraft/jsch/0.1.49 publishSiteFromCloudBeesToEC2
Buildfile: /scratch/jenkins/workspace/Publish site/KahunaSite/scripts/build.xml

publishSiteFromCloudBeesToEC2:
      [scp] Connecting to ec2-12-34-56-78.compute-1.amazonaws.com:22
      [scp] done.

BUILD SUCCESSFUL
Total time: 5 seconds

I hope this is useful -- it took me an embarrassingly long time to figure this out.

Comments