Cloud Foundry the API Way
This is a topic I’ve seen come up a couple times in the last few weeks. It started with Dr. Nic Williams when we were discussing share-my-cloudfoundry when he wanted to provide compatibility with Cloud Foundry v1 and v2 in the same application. This situation came up again with a personal project that I will detail later.
It required a bit of discussion, but I finally tracked down the answers. Although the cfoundry gem states that it is compatible with Cloud Foundry v1 and v2, after some digging, it looks like it’s only compatible with v2. To make things a bit more complicated, even though the old cfoundry library was moved to a separate repository, it retained the “cfoundry” gem name, meaning I could not include both gems in a single Gemfile.
Finally, Nic pointed out cloudfoundry-client developed by Ferran “ferdy” Rodenas, another Cloud Foundry superstar. I did a bit of playing around and it seems to fit my needs for Cloud Foundry v1. Combined with the currently maintained cfoundry gem for v2 compatibility, we’ve found the solution to our problem.
cloudfoundry-client’s tests are written just as I like to write my tests, so I was able to read through those to understand the usage of the gem. The cfoundry gem has great tests as well, but also has a piece in the Cloud Foundry Docs. Unfortunately, they don’t seem to be 100% up to date. So, I’ve decided to provide the code I used to push an application to both Cloud Foundry v1 and v2, explaining it along the way.
Preparing the App
In both Cloud Foundry v1 and v2, the vmc and cf CLI utilities create a zip file to upload to the environment. We need to handle this ourselves, which can be done with some simple code:
There’s some code here specific to me, but you can reference the zip gem. The important thing here is that the top level of the zip file is exactly as your current directory would be when pushing you application to Cloud Foundry. The other disclaimer is that this little chunk was modified from the CF Docs fairly quickly, so not guarantees this specific piece of code is production ready.
The application itself doesn’t particularly matter, but in my example code, I’ll be pushing a Sinatra application. Here are the relevant pieces of code:
The config.ru file is only needed for CFv2, but this app will work on both CFv1 as well as v2.
Pushing to Cloud Foundry v1
The code to push an application to CFv1 is actually very simple
Most of this code is self-explanatory, except maybe the manifest hash. You’ll notice some of these fields don’t match up with what you may be used to in a manifest.yml file. This is the format that the Cloud Foundry API expects, but I think once you see an example, it becomes pretty easy to know how to modify this as needed. The rest of the calls are mostly one-liners, except for actually starting the app. This is achieved in the CF API by setting the applications state to “STARTED”. Now that the cloudfoundry-client gem is a part of the cloudfoundry-community project, I may spend some time adding some helper methods to the API to ease some of these things.
Pushing to Cloud Foundry v2
Pushing an application to CFv2 is a bit more complicated, so we’ll break this out into chunks of code instead of looking at it all at once. First, we want to log in and create the application
The v2 gem doesn’t take a hash like the v1 gem does, but rather treats the application as an object. So far we haven’t done anything crazy, just simply target our CFv2 endpoint, logged in, and created a shell of an application. The next step is to create the route to map to our application:
So far, so good, but our app in CF isn’t really an app yet, but all of the pieces are in place. We can now upload our application:
Here we simply pass the path to the zip file of our application to the #upload method on the app object. And finally we need start the app:
That’s great and all, but what about all of that awesome information that CF spits out while staging and starting our application? Plus this is an async method, how can we get any idea on the progress of this? Well, the #start method actually can take in a block of code. Here’s another example of how to start an app:
Now when we start our application, we’ll see that same output that we get when using the cf CLI utility:
You’ll notice that CFv2 complains about not specifying a version of Ruby to use in our Gemfile. I’ve left that out because in CFv1, it actually errors out if you DO specify a version of Ruby to use.
I’m not sure how many people will need CFv1 and CFv2 compatibility in their projects, but what we’ve seen is both APIs are very friendly to use, especially if you’re familiar with the CF APIs. As I mentioned, cloudfoundry-client is now a part of the cloudfoundry-community organization, so if you feel like contributing to the v1 compatibility, you can find the repository on Github.
Let me know what you think of this article on twitter @brianmmcclain or leave a comment below!