You can find our new Cloud Foundry CLI, written in Go on github.
A complete rewrite, and yes, we changed things
We hadn’t been happy with the Ruby CLI for a while. We had three big problems: it was hard to test drive, it was hard to understand, it was hard to make changes. It was time for a rewrite. We also wanted to stay sensitive to the feedback we had received from the community and incorporate those learnings into the new CLI. This means that the command names changed as did the arguments and output, but retained all the previous functionality for interacting with your cloud and then some.
We decided to aim for a CLI that was easily scriptable so you can use it as part of your deploy scripts. This means that one command does one thing, and you can use it without being prompted for input. We also put a significant amount of effort in to an experience Ted calls “The Golden On-Ramp”, meaning that the first-time users who need to login and push a new app receive help getting up and running. And lastly, we made it easy for you to get in and see what’s going on in the code, so you can submit your own commands via Github pull requests.
Here’s some documentation on some of the differences between the Ruby cf v5 and the new cf v6 beta.
Why we picked Golang
One of the pain points around the Ruby CLI was that it was really easy to distribute to other Ruby developers, but painful otherwise. It relied on Rubygems, which required Ruby and a compiler – none of which ships on a Windows box. Even on OSX, prior to Mavericks the bundled Ruby version was unsupported. We worked around some of this using the ocra gem to package up the Ruby CLI in to a Windows binary, but we wanted something better. We decided the best course of action was to distribute a binary, instead of an interpreted environment.
Meanwhile, Golang has been gaining traction as a language that is designed for highly concurrent applications. Within Cloud Foundry we’ve already got several components that are written in Go: the router, distributed logging system, health manager, and we’re working on more. Because Go is a language we already know, and has the great advantage of being able to create cross-compiled binaries, it seemed like the perfect match!
“Two jobs is for two people” – the wisest short-order cook Scott has ever known
Without a design philosophy, everything was fair game for the Ruby CLI and it turned in to a tough codebase. In the rewrite we set out to have boundaries to inform our features and draw out a constructive compromise that accounted for code health.
- Each command does one thing, keep the Cloud Controller API as “naked” as possible.
- Push as many data validations as possible up to the Cloud Controller
- The exceptions are
push, which form “The Golden Onramp” helping every developer to learn Cloud Foundry and quickly get deployed.
Once we were committed to being a “dumb” client of the Cloud Controller, we discovered that whenever we felt the need to be “smart” we could instead make a compelling case that the functionality really belonged in the Cloud Controller. Our hope is that this will improve the API as well as the CLI, and make it easier for other parties to write their own clients and IDE plugins.
We struggled a lot with the high level of interactivity that
cf login offers and the assumptions that
cf push makes. It’s a big deviation from the non-interactive, non-assuming style the other commands employ. In the end, we want users to be happy, we want to let them get on with their work, and we want to surprise them as little as the system allows.
Written to be scripted
We realize we won’t nail everyone’s workflow, nor can we. We also want this to be a handy tool when you’re writing your own deploy scripts. With the plugin system, you had to write Ruby, and we want you to be able to integrate Cloud Foundry with whatever your existing deployment technology happens to be, especially since Cloud Foundry itself lets you deploy apps in any language.
In order to support scripting, we want to ensure that the CLI has a regular, predictable interface. We’ve adopted the following patterns to facilitate this:
- Exit Codes: 0 for success -OR- no action needed to be taken, 1 if there was an error
- Idempotent: When you execute a command, and no action needed to be taken, we’ll warn you, but return successfully. This enables your script to be run over and over again.
- Can use any command without interaction: Every command offers a flags for all of the needed inputs, there is no concept of a –no-interaction or –scripting flag that changes the behavior of the command.
Currently, there is one interactive command:
login prompts the user for any missing information, and attempts to automatically fill in values from the config file and Cloud Controller. This is helpful for humans, but may cause unexpected behavior for scripts. Instead of
login, script the sequence
target, to skip the magic.
You may be quick to notice that unlike the Ruby CLI there is no support for plugins. Because we are moving away from requiring you to write Ruby – or any particular programing language – that also means moving away from the existing plugin system. We are considering git-style plugins, and would like input from the community as to the specifics, and whether it is needed given that you can now easily script the CLI.
The Ruby CLI also had fairly extensive support for a YAML manifest file that accomplishes several things:
- Cross space/org deployments
- Deploying multiple apps with shared code base
- Deploying multiple apps that have different code bases, but talk to each other (i.e. publish/subscribe) and the order of deploy matters
- Ease of deployment/bootstrapping such as an open source project that wants to make it easy to get started on Cloud Foundry.
- Interaction-free pushing of apps
We do plan to add support for Ruby CLI generated manifest files in the coming weeks. However, many of us feel that a YAML DSL is currently limited in terms of what kinds of deployments can be accomplished relative to what is possible with an easily scriptable CLI.
Getting your hands dirty
Sometimes it makes sense for something to be part of the core CLI, rather than a script. We expect it to be easier to submit pull-requests now that we have a clean codebase with very consistent patterns. We’ve also set a goal to keep the commands and API calls isolated from each other to make future enhancements more straight forward. You should be able to create new commands simply by copying an existing command as a starting place. Engineers at Pivotal have been quick to pick up Go, and we hope you find it enjoyable as well.
About the Author