First Look at the new BOSH CLI

The new and improved version 2 of the bosh CLI has some great features that make deploying a bosh director (among other things) a breeze.

This document will walk through the steps of deploying a bosh director to a vSphere environment using the v2 CLI.

Install CLI v2

The first step for those following along is to make sure you have v2 of the CLI.

Downloads for Mac and Linux are available on bosh.io.

Alternatively, you can use Homebrew on macOS: brew install cloudfoundry/tap/bosh-cli.

Note: Homebrew installs the binary as bosh2 - you’ll want to alias it or replace all bosh commands in this document with bosh2 if you go this route.

Make sure bosh -v shows a version number starting with 2:

$ bosh -v
version 2.0.17-ef55792-2017-05-11T20:37:11Z

Succeeded

Deployment Manifest

Deployments in bosh are described with deployment manifests. Thankfully, there are a variety of open source templates available to get you started. You can obtain them from https://github.com/cloudfoundry/bosh-deployment.

For a vSphere deployment, you’ll need:

  • bosh-deployment/bosh.yml
  • bosh-deployment/vsphere/cpi.yml

I chose to copy these files out into a separate working directory to reduce clutter, but this step is optional.

If you’ve never used the v2 CLI, you may find a few unfamiliar items in the bosh.yml manifest that you downloaded. Specifically, there is a variables section towards the bottom, and a variety of variables scattered throughout the manifest. They look like this:

   password: ((admin_password))

With the new CLI we can store the values of these fields outside the deployment manifest. This allows us to share and reuse the deployment manifest without:

  1. leaking sensitive information.
  2. changing environment-specific details

The v2 CLI performs a process called variable interpolation to replace these placeholders with concrete values. You can specify these variables directly on the command line with the -v flag (-v admin_password=password), but I find it easier to store them in separate YAML files.

I created two files, misc.yml for misc properties, and vsphere.yml for the IaaS-specific properties.

misc.yml is pretty straightforward:

---
director_name: bosh-1
internal_cidr: 10.0.0.0/24
internal_gw: 10.0.0.1
internal_ip: 10.0.0.6
network_name: 'VM Network'

The only gotcha here is the single quotes around 'VM Network' since there’s a space in the network name.

vsphere.yml will require values specific to your environment:

---
vcenter_dc: Datacenter
vcenter_ds: <redacted>
vcenter_ip: <redacted>
vcenter_user: <redacted>
vcenter_password: <redacted>
vcenter_templates: bosh-1-templates
vcenter_vms: bosh-1-vms
vcenter_disks: bosh-1-disks
vcenter_cluster: Cluster

Note: in addition to reading variables from the command line and from files the CLI can also get them from environment variables, and in the future from a configuration server.

Create Environment

With these files in place, you’re ready to deploy.

The create-env command is roughly analogous to bosh-init:

$ bosh create-env bosh.yml -l vsphere.yml -l misc.yml -o cpi.yml --vars-store creds.yml

A brief description of this command:

  • bosh.yml is the deployment manifest, pulled from the bosh-deployment repo
  • the -l option loads variable values from our YAML files
  • -o cpi.yml specifies an operations file (more on this next)
  • the --vars-store specifies a read/write store for variables

The variables section at the bottom of the deployment manifest explicitly declares several variables and their types. This allows the CLI to generate them automatically if they are not provided. In this case, creds.yml doesn’t need to exist because we’re providing all required variables in the other files.

Operations Files

A major goal of CLI v2 is to make deployment manifests more portable (creating them from scratch is a pain!) Variable interpolation works great for simple placeholders, but sometimes you need to perform more sophisticated transformations. This is where operations files come in.

Operations files are a way to specify transformations to a deployment manifest at the time of deployment. This eliminates the need to reapply the transformations whenever the base document changes.

For this deployment the configuration for the vSphere CPI is specified in an operations file, which keeps the deployment manifest IaaS-agnostic.

Alias Environment

Once you’ve created an environment, you’ll want to create an alias for it to simplify future commands.

$ bosh alias-env bosh-1 -e 10.0.0.6 --ca-cert <(bosh int ./creds.yml --path /director_ssl/ca)
Using environment '10.0.0.6' as anonymous user

Name      bosh-1
UUID      f7e6bbd5-d4d2-4328-88f1-e5a549183a24
Version   261.4.0 (00000000)
CPI       vsphere_cpi
Features  compiled_package_cache: disabled
          config_server: disabled
          dns: disabled
          snapshots: disabled
User      (not logged in)

Succeeded

Take note of the bosh int command, which was used here to retrieve the generated CA cert from the creds.yml file. You can view this new environment with bosh environments, (or bosh envs for short):

$ bosh envs
URL             Alias
10.0.0.6        bosh-1

1 environments

Succeeded

From now on, you can use the -e flag with the bosh command to target this environment.

For example, you can use the cloud config from bosh-deployments/vsphere/cloud-config.yml:

$ bosh -e bosh-1 update-cloud-config -l misc.yml -l vsphere.yml cloud-config.yml

Gotchas

For the most part, this was a pretty painless exercise and definitely beats tweaking bosh-init manifests by hand.

There were a few things that caught me by surprise that you should look out for.

First, the default bosh.yml that we used from the bosh-deployments repo uses Google’s public DNS. You may need to tweak this for your environment.

More importantly, the user experience is lacking when you omit variables, either by forgetting to supply them on the command line or by loading them from a file. The CLI ends up leaving the ((variable)) placeholder in the manifest and attempting to deploy, resulting in an error that can be difficult to troubleshoot.

There’s plenty of discussion on the github issues, but the BOSH team seems hesitant to address the issue at this point, as their long term plan is to also allow the director to perform variable interpolation.

The bosh interpolate command takes --var-errs and --var-errs-unused flags which causes the interpolation to fail if variables are missing or unused respectively. This is helpful for troubleshooting, but bosh deploy and bosh create-env don’t have similar flags. Until this area improves, I’d recommend running a bosh int --var-errs when you encounter an odd error - it may just point you to a missing variable.

Summary

A few important points from this exercise:

We used variable interpolation and operations files to deploy a bosh director without ever modifying a deployment manifest. Instead of generating our own manifest, we were able to use one that was provided for us, applying customizations in a non-destructive manner.

We didn’t need to make use of bosh-init. The V2 CLI’s create-env command is a suitable replacement.

The CLI was able to generate passwords and certs for us and store them in a file. This is great for a manifest consumer. Additionally, the manifest contained a list of variables and their types. This is great for a manifest author as it allows them to specify their requirements in a way that makes them easy to satisfy.

Introducing gogetdoc, a tool for providing Go documentation to editors

I really enjoy Go’s approach to documentation. The conventions are simple (and almost obvious), and the tooling around them is great. If you’re not familiar with Godoc, I’d recommend reading the Go team’s official blog post on the topic.

Why Another Tool?

There are no shortage of tools available for looking up Go documentation, but many of these tools lack a good interface for editor integration.

For example, the go doc tool is very easy to use from the command line:

go doc io            # get doc for package io
go doc io Writer     # get doc for io.Writer interface
go doc fmt.Println   # get doc for Println func in package fmt

Modern IDEs typically provide functionality that allows the user select some source code and get documentation for their selection. There are several editors that provide similar features for Go development, but these tools are forced to use sub-optimal workarounds due to go doc’s interface. Additionally, since each editor comes up with its own tricks, the capabilities and results from different editors differ drastically.

The problem with the command line interface is that it is intended to be used by a human who knows what they are looking for. This makes the editor responsible for figuring out what the user is looking for, which turns out to be harder than you’d think.

Let’s look at a few popular editors for Go devleopment.

Vim-Go

Vim-Go uses a clever approach - it adds the ‘.’ character to the list of characters that can appear in a word, and then gets the word under the cursor. For example, this would return fmt.Println as a single word. This word is then split at the ‘.’, giving you one or two tokens to pass to go doc.

This approach works in a lot of cases. Several months ago I attempted to add documentation support to the go-plus Atom package using this technique. I quickly realized that there are a lot of special cases that this approach cannot handle.

For example, if you attempt to get the documentation for a method call on a variable, you’d end up passing a variable name to the go doc tool, which won’t work.

Vim-Go method call

Similarly, documentation for struct fields will end up using a variable name as a package name:

Vim-Go struct field

If you import a package under a different name, you’ll end up giving go doc a package it doesn’t know about.

Vim-Go package alias

Sublime Text (GoSublime)

I’m not too familiar with how GoSublime works, but I was able to fire it up and give it a try. I found that the documentation support worked quite well, even for several of the cases that failed with Vim-Go.

Unfortunately, method calls still didn’t work.

GoSublime method call

gogetdoc Features

Hopefully by now you’re convinced that there’s room for improvement in this area.

I’ve been working on a tool that uses the go/types packge to identify the item under the cursor and get documentation for it.

gogetdoc has a simple command line interface that accepts a file and (byte) position, similar to many other Go tools:

$ gogetdoc -pos $GOROOT/src/fmt/format.go:#6272
func unicode/utf8.RuneCountInString(s string) (n int)

RuneCountInString is like RuneCount but its input is a string.

It can provide documentation for:

  • packages (from import specs)
  • constants and variables (including constant values)
  • function calls (including method calls)
  • identifiers in packages imported under different names (ex import format "fmt")
  • struct fields

Update!

gogetdoc has seen a few great contributions over the past week, including:

  • performance improvements
  • support for modified files (using guru’s archive format)
  • more tolerance for parse errors

Additionally, both vim-go and Emacs have started adding support for the tool!

Atom Plugin

I put together a rudimentary Atom package in order to demonstrate the features of this tool. You can find it at https://atom.io.packages/godoc. Make sure to install the tool first: go get -u github.com/zmb3/gogetdoc. (Note that gogetdoc requires Go 1.6 to build - this is due to some minor API differences between golang.org/x/tools/go/types and go/types.)

Here are a few screenshots of it in action.

Package documentation from import specs:

Atom package doc

Constants, including their value:

Atom constants

Method calls:

Atom method call

Imports under a different name:

Atom import alias

Struct fields:

Atom struct field

Contributions/Roadmap

These tools aren’t quite ready for prime time, but I don’t think they’re too far off. If you enjoy working on tools to help other developers, I’d love your help. The gogetdoc tool and the Atom package could both use some love.

I’m particularly interested in the following:

For gogetdoc, I would like to implement more structured output. In it’s current form, editors really only have one choice - to display the tool’s output verbatim. If the tool were able to output JSON, for example, it could provide a title, object type (func, var, package, const, etc.), and the documentation separately. This would give editors a little more flexibility as to how to design their UI. Other examples of different output formats include HTML or even Go template language.

Additionally, anything that improves performance would be a welcome addition. I’ve found the performance acceptable so far, but I haven’t tried it on very large projects.

The godoc Atom package could use a UI overhaul. My frontend skills aren’t very good. I’d love to see a nicer tooltip window with scrollbars and a close icon to dismiss it.

Gopath Janitor

I put together a simple tool last week that you may find useful.

The idea is that over time, your GOPATH can become cluttered with all of the various items you’ve acquired via go get. The go tool does a nice job at fetching the dependencies of commands for you, so getting the command and its source installed is a piece of cake.
Removing a command is not so easy. Sure, you can remove $GOPATH/src/<CMD> and $GOPATH/bin/<CMD>, but what about all of those dependencies?
Wouldn’t it be nice if you could instantly locate unused packages in your GOPATH?

This may not be a new idea, but I did a quick search and couldn’t find such a tool, so I decided to build it. The tool is called gpj, short for Gopath Janitor, and you can find it on github.

There’s not much to it - the go/build package can do most of the work for you. All I had to do was gather up all of the “library” packages, and then loop through all packages in GOPATH, marking each package’s imports as being used. Any library packages that aren’t marked as imported get reported to the user.

I used the neat asciinema tool to generate an example session. Check it out:

Example

Future Enhancements

In the future, I’d like to do a better job with internal and vendored packages. For now, the tool assumes that you don’t want to hear about these types of packages.

One last thing to note is that you shouldn’t just go removing any directories that are displayed in the gpj output. It’s important to understand that gpj is looking for unimported packages. Many repositories consist of multiple packages. If you have a dependency on one of those packages, go get is going to grab the entire repository and you’ll end up with some unimported packages in your GOPATH.