I was inspired by Brian McClain’s post on bringing Haskell to Cloud Foundry using Cloud Foundry v2 buildpacks, so I decided to go on a buildpack journey of my own. Since Clojure is the language I most enjoying “toying around with,” I thought I’d try to deploy a simple Clojure web application using the Heroku Clojure Buildpack.
To reiterate some of the coolness around buildpacks, they are what allows a PaaS like Cloud Foundry or Heroku to support various runtimes without first building that support into the core platform. If your favorite language or framework runtime isn’t available, there’s absolutely nothing stopping you from providing your own buildpack to support it. Stuff can get crazy – McClain has even hinted at bringing FORTRAN support to Cloud Foundry.
I decided for my experiment to build a VERY basic “Hello World” style application using Ring, which is “a Clojure web applications library inspired by Python’s WSGI and Ruby’s Rack.” The easiest way to get started building Ring applications is to utilize the popular Clojure build tool Leiningen.
First I started by creating a new project:
12
$ lein new hello-cf
$ cd hello-cf
The next task was to add the Ring dependencies to my project.clj file:
12345678
(defproject hello-cf"0.1.0-SNAPSHOT":min-lein-version"2.0.0":description"Hello Clojure on Cloud Foundry!":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.4.0"][ring/ring-core"1.1.8"][ring/ring-jetty-adapter"1.1.8"]])
Then it was time to create the Ring application itself, by editing src/hello_cf/core.clj:
12345678910
(ns hello-cf.core(:use[ring.adapter.jetty:only[run-jetty]]))(defn handler[request]{:status200:headers{"Content-Type""text/html"}:body"Hello Cloud Foundry from heroku-buildpack-clojure!"})(defn -main[port](run-jettyhandler{:port(Integer.port)}))
Let’s break this down a bit. The handler function will handle any HTTP request that hits our application, and will return an “OK” response containing a pleasant message indicating that we’ve succeeded. That’s really about it. Our application is complete. We can test it out by running the following:
Hitting http://localhost:8080 in the browser confirms that we’re well on our way. Now it’s time to trying pushing the application to Cloud Foundry. As Brian stated in his blog, one of the stellar aspects of Cloud Foundry buildpacks is that they are approximately the same as Heroku buildpacks. Practically, this means that one should be able to utilize a Heroku buildpack on Cloud Foundry with minimal or no modifications. Let’s put that theory to the test, shall we? Before we do, let’s create a Procfile quickly to let the buildpack know what we want to run:
1
web: lein with-profile production trampoline run -m hello-cf.core $PORT
cf push hello-cf --buildpack=git://github.com/heroku/heroku-buildpack-clojure.git
Using manifest file manifest.yml
Creating hello-cf... OK
1: hello-cf
2: none
Subdomain> hello-cf
1: mstine.cf-app.com
2: none
Domain> mstine.cf-app.com
Binding hello-cf.mstine.cf-app.com to hello-cf... OK
Uploading hello-cf... OK
Starting hello-cf... OK
-----> Downloaded app package (12K)Initialized empty Git repository in /tmp/buildpacks/heroku-buildpack-clojure.git/.git/
Installing heroku-buildpack-clojure.git.
-----> Installing OpenJDK 1.6...done
-----> Installing Leiningen
Downloading: leiningen-2.1.2-standalone.jar
Writing: lein script
-----> Building with Leiningen
Running: lein with-profile production compile :all
Retrieving lein-standalone-repl/lein-standalone-repl/0.1.5/lein-standalone-repl-0.1.5.pom from clojars
Retrieving lein-standalone-repl/lein-standalone-repl/0.1.5/lein-standalone-repl-0.1.5.jar from clojars
Performing task 'compile' with profile(s): 'production' Retrieving org/clojure/clojure/1.4.0/clojure-1.4.0.pom from
...
Compiling hello-cf.core
-----> Uploading staged droplet (66M)-----> Uploaded droplet
Checking hello-cf...
Staging in progress...
Staging in progress...
Staging in progress...
Staging in progress...
Staging in progress...
Staging in progress...
Staging in progress...
0/1 instances: 1 starting
0/1 instances: 1 down
0/1 instances: 1 starting
0/1 instances: 1 flapping
Application failed to start.
Drat. Let’s take a quick look at the logs to see what may be awry:
12
Reading logs/stderr.log... OK
/home/vcap/app/.lein/bin/lein: line 42: java: command not found
Ah-hah! Looks like the existing buildpack is making some assumptions about the structure of our application that no longer hold true on Cloud Foundry. So, I followed in Brian’s footsteps and forked away. One small commit looks like it ought to fix the problem. Let’s give it another try:
Score another win for Cloud Foundry’s buildpack support. I’m now toying with the idea of doing something of a world tour of LISP on Cloud Foundry. My next candidate may be Scheme.
My passion is taking a metaphysical approach to software engineering: what is the nature of the collaborative game that we continuously play, and are there better, more contextually-aware ways to play that game?
By day I lead a team tasked with taking a first-principles-centric approach to intentionally enabling programming language usage at the largest bank in the United States.
By night I write and teach my way through a masterclass in software engineering and architecture targeting early-career software engineers working in large-scale enterprise technology organizations.
What is the primary goal?
To win the game. More seriously: to get 1% better every day at providing business value through software.
Who am I?
I'm a 22-year veteran of the enterprise software industry. I've played almost every role I can imagine:
Software Engineer
Software Architect
Technical Lead
Engineering Manager
Consultant
Product Manager
Field CTO
Developer Advocate
Conference Speaker
Author
Technical Trainer
Technical Marketer
Site Reliability Engineer
Desktop Support Specialist
I've worked at Fortune 500 companies, a tenacious teal cloud startup, and a not-for-profit children's hospital. I've written a book, and I've hosted a podcast. I've learned a lot along the way, including many things I wish I'd known when I first got started. And so now I want to pass those learnings on to you, especially if you've only just begun your career.