Deltacloud sings a new tune

04 March 2010

A few weeks ago, I came across Sinatra, a minimalist Ruby web framework, much leaner, meaner and simpler than Rails — while it’s probably not a good fit for traditional database-backed web applications, it seemed like an ideal framework for Deltacloud Core.

Deltacloud Core is a cross-cloud API providing abstraction and compatibility across multiple clouds. There are quite a few such API’s out there; what makes Deltacloud unique is that it is not a client-side library tied to a specific language. Instead, it is a RESTful web service, initially written in Rails by Bob McWhirter.

While Rails was a good initial framework for Detlacloud Core, and is the default Ruby web framework for a reason, there’s a few things that make it overkill for a simple webservice:

  • Deltacloud Core is stateless, and therefore has no need to store anything in a database; that means we’ll never use Rails’ ActiveRecord
  • Deltacloud Core isn’t really a MVC app; it’s simply an adapter around existing API’s, and the split of responsibility that MVC implies doesn’t really fit with how an API adapter works.
  • Deltacloud Core hooks into a few places in the handling of requests, most importantly to do parameter validation based on the operation being performed in the API.

Of course, the biggest draw of Sinatra is that it concentrates all the controller code in one place (currently ~ 300 lines in server.rb) which makes it much easier to understand how the whole application is put together.

Sinatra Additions

There were two areas where Sinatra out-of-the-box was falling short on important functionality: RESTful request validation and responding in a variety of content types.

Michal and I took care of request validation with a small custom DSL for describing RESTful operations, their parameters etc. cleverly called Rabbit. That DSL lets us talk about collections and operations on collections, and generates validation logic and online documentation on-the-fly. You can browse an instance of Deltacloud Core and immediately see what operations are available, for example, on instances, and what parameters the create instance operation accepts. (Why and how that is important is a topic for another blog post)

Deltacloud Core can be used in two ways: you can either point your web browser at it and browse a variety of information about the objects you have in a specific cloud, and you can make RESTful requests that return XML responses. That dual nature makes it necessary to respond to some requests with HTML, and to others with XML. Sinatra doesn’t have a mechanism that’s as convenient as Rails’ respond_to for this, though there’s a nice extension that does exactly that, so that we could write listing of a collection (model in the code below), simply as

singular = model.to_s.singularize.to_sym
@elements = driver.send(model.to_sym, credentials, filter)
instance_variable_set(:"@#{model}", @elements)
respond_to do |format|
  format.xml  { return convert_to_xml(singular, @elements) }
  format.html { haml :"#{model}/index" }
end

The Sinatra respond_to extension actually didn’t do content negotiation based on the Accept header; we added that based on a couple of existing Rack extensions. And discovered in the process that the Accept header that Chrome sends is utterly insane.

All in all, the move to Sinatra worked out very well; existing functionality was easy to preserve, and we added validation and self-documentation to Deltacloud Core, both in human-readable format and as (experimental) XML.