This blog post provides an update and a tutorial from the Cloud Foundry CAPI team on app revisions with Cloud Foundry V3 API.
Introduction
With the release of CAPI 1.79.0, we introduced the revisions feature. When you enable revisions for your app, Cloud Controller will create a new revision any time new code, environment variables, or process commands are changed and deployed. Previously, Cloud Foundry did not keep a record of an application’s history. With revisions, you can now see what changed with your application (and when). Perhaps more importantly, if you find that your most recent deployment contained a serious bug, you can now rollback to a previous “good” version.
In this article, we will explore in-depth the functionality of revisions in Cloud Foundry, including what exactly they track, how to view your revision history, and how to revert to a previous revision.
For this tutorial, we will be using this sample app: CF Ruby Sample App. If you would like to follow along, you can clone this repository, or use your own test application.
Basic Functionality
Let’s start by creating our application. In the app’s root directory, let’s run:
cf v3-create-app sample-app
Currently, revisions are enabled on a per-app basis. To enable revisions for our sample application, let’s enable the feature flag via the Cloud Controller API:
cf curl /v3/apps/$(cf app sample-app --guid)/features/revisions -X PATCH -d '{ "enabled": true }'
Now that we have created our app and enabled revisions, we can poll the v3/revisions endpoint to watch the new revisions being created as we deploy different versions of our application.
In a separate terminal, run:
watch 'cf curl /v3/apps/$(cf app sample-app --guid)/revisions | grep description'
(For the purposes of this tutorial, we are only extracting the description of the revisions, as the raw API results would be too verbose).
To begin with, we won’t see any results, as we haven’t deployed our app yet. So let’s try it! In your app directory, run:
cf v3-push sample-app
Partway through the push process, you should see a description of your app’s first revision appear:
"description": "Initial revision."
Our first revision!
Because this is the very first one, its description is not too informative. But let’s try changing an environment variable:
cf set-env sample-app FOO BAR
cf v3-restart sample-app
You’ll see a new revision is created, explaining the change:
"description": "Initial revision.",
"description": "New environment variables deployed.",
This description is a little more helpful.
Now, let’s try re-pushing the app without any changes:
cf v3-push sample-app
You might expect that would be no revision, as we didn’t make any changes, right? But you will see that a new revision was created:
“description": "Initial revision.",
“description": "New environment variables deployed.",
“description": "New droplet deployed.",
That’s because pushing always creates a new droplet, even if there are no changes.
More Complex Changes
Now let’s take a detailed look at the app’s current revision. Conveniently, we can fetch the currently deployed revisions by calling a special endpoint,
v3/apps/:app_guid/revisions/deployed:
cf curl v3/apps/$(cf app sample-app --guid)/revisions/deployed
Your revision will look something like this:
{
"guid": "ac8f9f3b-ddd1-4385-8960-c464e2c3feb9",
"version": 5,
"droplet": {
"guid": "0ae58601-5687-4afd-9b73-97759e651a3d"
},
"processes": {
"web": {
"command": null
}
},
"description": "New droplet deployed.",
"created_at": "2019-05-20T23:36:35Z",
updated_at": "2019-05-20T23:36:35Z",
"links": {
"environment_variables": {
"href": "https://api.moor-cap.capi.land/v3/revisions/ac8f9f3b-ddd1-4385-8960-c464e2c3feb9/environment_variables"
}
}
...
}
As you can see, revisions track droplets and process commands, as well as the app’s environment variables. The “web” process’s command being set to null means that it defaults to the start command defined in that droplet’s Procfile. But we can override that command using the V3 API. Let’s try it!
First we get the app’s web process:
cf curl v3/apps/$(cf app sample-app --guid)/processes/web
The result will look something like this:
{
guid": "MY_GUID",
"type": "web",
"command": "bundle exec rackup config.ru -p $PORT",
"instances": 1,
"memory_in_mb": 256,
"disk_in_mb": 1024,
"health_check": {
"type": "port",
"data": {
"timeout": null,
invocation_timeout": null
}
},
...
Let’s take the guid from the top of the payload and alter the process’s start command slightly by PATCHing it:
cf curl /v3/processes/MY_GUID -X PATCH -d '{ "command": "FOO=test ruby app.rb" }'
cf v3-push sample-app
You should see yet another revision description appear:
"description": "Initial revision.",
"description": "New environment variables deployed.",
“description": "New droplet deployed.",
“description": "Custom start command updated for 'web' process. New droplet deployed.",
If you curl the deployed revision again, you’ll see that the revision has recorded the custom start command.
Rolling Back to Previous Revisions
But let’s say we decide we want to undo the changes we’ve made, and revert to our first revision. We can query for our first revision of the app using the version query parameter.
cf curl /v3/apps/$(cf app sample-app --guid)/revisions?versions=1
Grab the revision’s GUID, and then create a new deployment via the V3 API:
(You’ll also need to grab the app’s guid with cf app sample-app –guid)
cf curl /v3/deployments -X POST -d '{
"revision": {
"guid": "REVISION_GUID"
},
"relationships": {
"app": {
"data": {
"guid": "APP_GUID"
}
}
}
}'
You should see yet another description appear in your other terminal:
"description": "Initial revision.",
"description": "New environment variables deployed.",
"description": "New droplet deployed.",
"description": "Custom start command updated for 'web' process. New droplet deployed.",
"description": "Custom start command removed for 'web' process. New droplet deployed. New environment variables deployed. Rolled back to revision 1.",
As you can see, all the changes we made were undone, and we’ve reverted our application back to our initial state!
NOTE: There are some limitations on rolling back to previous revisions. Most notably, by default Cloud Controller will only store the five most recent droplets for an app. This can be configured with the cc.droplets.max_staged_droplets_stored
property. Otherwise, by default Cloud Controller will only store the last 100 revisions. However, this can be configured with the cc.max_retained_revisions_per_app
property.
Conclusion
For more information about revisions, please refer to the Dev Guide and the V3 API. Please note this feature is still considered experimental and not yet recommended for production environments. Please let us know your thoughts and feedback in the #capi channel in our Slack, and we hope you are excited to use revisions in your deployment process!
Our colleague Zach Robinson will be speaking on a panel at Cloud Foundry EU Summit in the Hague this September. Make sure to check out his session!