Rolling Out Clojure

At Box UK we try, where practical, to be technology agnostic and pick the best tool for the job. Over the next few years I see Clojure (and possibly other JVM languages like Scala) becoming more and more important in solving the problems that both we and our clients encounter.

Internally I’ve been trialling small Clojure applications and libraries for some time, but if we’re going to be rolling these out to production environments then we’re going to need a stable way to do it.

Build Problems

In our current deployment process, using RPMs for all our applications has become standard. But doing some casual Googling on how to get started building these for Clojure projects turned up very few results…There’s a plugin for Leiningen, but we use a slightly more custom process with Mock to create a clean chroot’d build environment, and I couldn’t see how this was going to work for us.

Another problem (before even being able to consider the previous plugin) is Leiningen itself. This is the de facto standard tool for managing Clojure projects, but unfortunately the packaged version is way out of date. This causes problems as our build servers need to be managed in the same way as all our other boxes: packaged software only please.

None of this is too challenging though, so I’ll go over what we put in place to effectively package Clojure applications.

How to package Clojure applications

Step 1 – Packaging Leiningen

The first step is packaging Leiningen. With the most recent version I could find being 1.7-something, we needed to roll our own, which we’ve packaged under the name boxuk-leiningen to avoid naming conflicts should an official package appear. Here’s the interesting bit:

%define _prefix /usr/share
 
%define _lein_version 2.1.3
 
%define _lein_jar leiningen-%{_lein_version}-standalone.jar
 
%define _lein_downloads https://leiningen.s3.amazonaws.com/downloads
 
%define _lein_repo https://raw.github.com/technomancy/leiningen
 
 
%build
 
curl -o %{_lein_jar}
         %{_lein_downloads}/%{_lein_jar}
 
curl %{_lein_repo}/%{_lein_version}/bin/lein
      | sed 's#LEIN_JAR=".*"#LEIN_JAR="%{_prefix}/%{name}/lib/%{_lein_jar}"#'
      > lein

We’re pulling down a specific version to install globally, and tweaking the shell script to reference it:

%install
 
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT%{_prefix}/%{name}/bin
mkdir -p $RPM_BUILD_ROOT%{_prefix}/%{name}/lib
 
cp lein $RPM_BUILD_ROOT%{_prefix}/%{name}/bin
cp %{_lein_jar} $RPM_BUILD_ROOT%{_prefix}/%{name}/lib
 
mkdir -p $RPM_BUILD_ROOT/etc/profile.d
cat <<EOF >$RPM_BUILD_ROOT/etc/profile.d/%{name}.sh
PATH=%{_prefix}/%{name}/bin:$PATH
export PATH
EOF

If someone tries to self-install they’ll probably get some unexpected behaviour…but at the moment we’re mainly just pulling this in for our RPM builds so it doesn’t matter too much.

Step 2 – Packaging individual applications

With Leiningen now available the second part is packaging individual applications. The setup we’ve started rolling with is far from perfect, but it’s a work in progress and something we will improve. The project is available on Github, so go check it out if you’re interested. Again, while it’s tailored slightly to our specific process, it should be a good starting point if this is your first attempt at doing this kind of thing.

First the spec file:

BuildRequires: java-1.6.0-openjdk-devel, boxuk-leiningen
 
%build
 
lein bin

As you can see, we pull in our custom Leiningen RPM as a build dependency. Rather than embedding web applications within a container like Tomcat we’re using the lein-bin plugin to create standalone ‘binaries’ for each application. This then packages all the dependencies and just requires Java on the servers being deployed to. It also lets the application decide which kind of web server it uses (if any of course).

The rest of the file is standard RPM stuff; setting the application up using chkconfig with an init script to manage it:

%install
 
rm -rf $RPM_BUILD_ROOT
 
mkdir -p $RPM_BUILD_ROOT%{_prefix}/%{name}/bin
cp target/%{name} $RPM_BUILD_ROOT%{_prefix}/%{name}/bin/
 
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/init.d/
cp %{_sourcedir}/chkconfig.conf $RPM_BUILD_ROOT%{_sysconfdir}/init.d/%{name}
 
chmod 755 $RPM_BUILD_ROOT%{_sysconfdir}/init.d/%{name}
 
%post
 
if [ "$1" = "1" ]; then
       chkconfig --add %{name}
fi
exit 0
 
%files
 
%defattr(-,root,root,-)
 
%attr(0755, root, root) /etc/init.d/%{name}
 
%attr(0744, root, root) %{_prefix}/%{name}/bin/%{name}
 
 
%pretrans
 
if [ -f %{_prefix}/%{name}/bin/%{name} ]; then
       service %{name} stop
       chkconfig %{name} off
fi

This then allows us to easily install and manage the application with the usual service start/stop/restart commands.

Conclusion

It’s a pretty simple thing, but this solution allows us to start rolling out internal Clojure apps and pilot projects where it makes sense.

The code is on Github:

https://github.com/boxuk/leiningen-rpm

https://github.com/boxuk/clojure-rpm-template

At Box UK we have a strong team of bespoke software consultants with more than two decades of bespoke software development experience. If you’re interested in finding out more about how we can help you, contact us on +44 (0)20 7439 1900 or email info@boxuk.com.