2017-02-09

Br-<TAB>-ooklyn Completion

One of the nice features of Docker 1.13 is the rationalisation of their CLI. Now the `docker` arguments are all neatly organised around a common schema making it easier to work out what command you need to use. The benefits of a well thought out CLI are enormous, particularly when automating tasks for CI pipelines or scheduled operations tasks. However the main drawback with textual interfaces like the command-line has always been discoverability. With a GUI the options are there in front of you, or at most hidden behind the click of a mouse on a menu or drop-down list. Sometimes it can feel like you are stuck back in the seventies with the worst of Unix elitism: what do you mean you didn't know that the -B flag can't be used when you set the depth to seven with the -S option, obviously you should have been using -Q 3 to force compatibility mode!

Fortunately, modern shells have the answer with tab completion. This is available for most of the built-in commands and usually supported by third party tools via a completion script that is either installed at the same time as the tool or can be downloaded or generated afterwards. So, let's look at improving things for users of Brooklyn and Cloudsoft AMP, with maybe some extra help for a few common container management systems as well.

Brooklyn has now been shipping with a command-line interface for the past few releases, written in Go and talking to our REST API. Due to the shared open-source heritage, all Brooklyn commands are also compatible with Cloudsoft AMP so assume that I am referring to both for the rest of this post.

The command-line client can be installed on Linux, OSX or Windows, and is available for download on the Brooklyn website. For example, to install on a Linux machine use the following commands:

% wget https://s.apache.org/VFt1 \
    -O apache-brooklyn-0.10.0-client-cli-linux.tar.gz
% tar zxf apache-brooklyn-0.10.0-client-cli-linux.tar.gz
% cd apache-brooklyn-0.10.0-client-cli-linux
% sudo cp br /usr/local/bin
% sudo chmod 755 /usr/local/bin/br

Once that is done, you can log in to a running Brooklyn server and perform all the tasks that the UI makes available, such as listing entities, starting and stopping applications, retrieving sensor values and executing effectors. For example, here we are connecting to Brooklyn and finding the host name and port number for connecting to a running web application:

% br login http://brooklyn.example.org:8081/ guest
Enter Password:
Connected to Brooklyn version 0.11.0-SNAPSHOT at http://brooklyn.example.org:8081
% br application
Id           | Name         | Status    | Location
t2nk41gcqg   | wordpress    | RUNNING   | xdvtwoprye
% br application t2nk41gcqg entity
Id           | Name       | Type
ve7qrj46zk   | mysql      | org.apache.brooklyn.entity.database.mysql.MySqlNode
aqapl0919b   | tomcat     | org.apache.brooklyn.entity.webapp.tomcat.TomcatServer
% br application t2nk41gcqg entity aqapl0919b sensor host.address
10.250.0.104
% br application t2nk41gcqg entity aqapl0919b sensor http.port
8080
% br application t2nk41gcqg entity aqapl0919b http.port.mapped.public
52.59.203.14:30456

Note that the last line was required because this application happened to be running on Docker, so we need to know the correct address to connect over the Internet, not the IP address and port the container is using internally.

Once we are happy that the application is working, perhaps by using this information to run curl and retrieve some HTML, we might no longer need to keep this instance running. Again, we can perform any task that is possible from the UI, in this case calling the stop effector:

% br application t2nk41gcqg stop
% br application t2nk41gcqg
Application 't2nk41gcqg' not found    404

So, this is all good so far, but we are not doing well on the discoverability side. There is help text available, as seen by running br help which outputs the following:

% br help
NAME:
   br - A Brooklyn command line client application
USAGE:
   br [global options] command [command options] [arguments...]

VERSION:
   0.10.0

...

But as well as this we can add bash auto-completion. This is not yet part of the Brooklyn codebase, and is strictly experimental at present, so the usual caveats apply. In fact, I created this project to learn more about how bash auto-completion technology works, so it is my very first implementation of one. The code is available on GitHub at brooklyncentral/brooklyn-bash-completion and consists of a single file, also named br.

The completion script is probably in need of some expert attention, and I would also like to add support for more of the Brooklyn commands, but it is definitely usable and I find it invaluable. I have also discovered that there are some features that could be added to br itself that would make implementing auto-completion much simpler. For instance, options for all the commands that cause it to return unadorned text, such as a list of entity IDs one per line, or being able to pass in Go template strings that would allow formatting lines of output in this way. Looking at the way that tools like kubectl works is instructive here, as it also support this feature and uses it in its completion script.

With all that said, here's how to get started with br auto-completion script for bash. First, make sure that auto-completion is supported, and actually installed! This means installing using MacPorts or HomeBrew on OSX; with Linux use your distribution's usual package manager. Install the bash-completion package, which should create a directory where completion scripts can be installed. This is usually something like /etc/bash_completion.d or on OSX /opt/local/etc/bash_completion.d but check the documentation or one of the many tutorials to be sure. Once you have determined the correct location, you simply copy the `br.sh` script there, and source it if you want to be able to use it without logging in again:

% wget https://git.io/vDzkZ -O br
% sudo cp br /etc/bash_completion.d
% . /etc/bash_completion.d/br

Although nothing seems to have happened, you now have the power! Implemented completions include command names, application and entity ids, sensor names and catalog and blueprint files. For example, this is how a deployment of a YAML blueprint to AWS would look:

% br dep<TAB>loy exam<TAB>ple.yaml
Id:       | i85xkp9i2c
Name:     | example
Status:   | In progress
% br app<TAB>lication i8<TAB>5xkp9i2c
Id:              | i85xkp9i2c
Name:            | example
Status:          | RUNNING
ServiceUp:       | true
Type:            | org.apache.brooklyn.entity.stock.BasicApplication
CatalogItemId:   | centos7-software-process:0.11.0-SNAPSHOT
LocationId:      | xdvtwoprye
LocationName:    | AWS Europe Central 1 with CentOS 7
LocationSpec:    | jclouds:aws-ec2
LocationType:    | org.apache.brooklyn.location.jclouds.JcloudsLocation
% br app<TAB>lication i8<TAB>5xkp9i2c se<TAB>nsor ser<TAB>vice.<TAB>
service.isUp              service.notUp.indicators  service.problems          service.state             service.state.expected
% br application i85xkp9i2c sensor service.is<TAB>Up
true

So, there we have it. As mentioned earlier, this was my first attempt at writing a completion helper script, and I think it needs a LOT more work. However, even in the rudimentary state it is in now, it makes my life a lot easier when using Brooklyn, and I hope that by getting more people to use it bugs will be discovered and fixed much faster.

And here are the promised Kubernetes instructions. The kubectl command actually comes with a self-installing bash completion script, which can be enabled for the current shell as follows:

% . <(kubectl completion bash)

To keep it around for your next login, save the output of the command and copy it to your completion directory as with the `br` instructions above, like this:

% kubectl completion bash > kubectl.sh
% sudo cp kubectl.sh /etc/bash_completion.d/kubectl.sh

I find this a huge time saver when debugging Kubernetes issues.

Finally, as we all know, The perfect is the enemy of the good and the br script is definitely not perfect, but I hope this post will encourage someone to take a look at the code, and perhaps move it somewhat closer to good territory... Simply clone the brooklyncental/brooklyn-bash-completion repository and file an issue or create a pull request! Eventually, I even hope that it could even improve to a state where it can be included with the Brooklyn command-line client.