Bash completion for Consul nodes on Ubuntu
After making the jump to Ubuntu as my preferred distribution, I've admittedly become addicted to bash completions (aka autocomplete, tab completion, typeahead). Bash completions provide immediate hints for common commands, and even the options associated with them.
For example, a quick \t\t
(double-tap of the tab key) after the service command will provide a quick snapshot of services available on the machine, beginning with your query.
someone@example:~$ sudo service a
acpid anacron apache2 atd
As with most things, seemingly small details, like typeahead completions, can make for a very useful productivity improvement.
Consul
I have been utilizing the Consul agent from HashiCorp for service discovery inside an AWS VPC. Consul is a well-thought-out, distributed, service discovery agent that accomplishes a few things quite elegantly:
- Determines location of services via CLI and DNS
- Keeps availability status up-to-date with health checks
- Stores facts that can be shared across instances
- Provides performant localhost access to everything
- Easy to setup, highly configurable, and resilient
Though individually these features seem pretty straight-forward, the combined elements of service discovery can be incredibly complex in dynamic environments, and challenging to keep synchronized. You can read more about Consul on their website.
Bash Completion
Bash completion on Ubuntu provides a configuration directory for adding your own complete
handlers. In this example, you will need to have the consul agent binary accessible in your path.
someone@example:~$ consul members
Node Address Status Type Build Protocol DC
dev-web 10.10.0.17:8301 alive server 0.6.4 2 dc-east
dev-mysql 10.10.0.44:8301 alive client 0.6.4 2 dc-east
qa-web 10.10.0.63:8301 failed client 0.6.4 2 dc-east
The consul members
command will provide a list of nodes alongside a status of: alive, failed, or left. You may choose to filter out different statuses from your completion. I only filter out left
because it indicates a proper shutdown and removal for my use case. The failed
status nodes are something I would still want access to.
We will create our completion script here:
# /etc/bash_completion.d/ssh_consul
With the following contents:
#!/bin/bash
_consul_members()
{
local cur
local res
cur=${COMP_WORDS[COMP_CWORD]}
res=$(consul members | grep -v ' left ' | tail -n +2 | cut -f1 -d ' ' | sort -u)
COMPREPLY=($(compgen -W "${res}" -- ${cur}))
}
complete -F _consul_members ssh
By default, Consul uses the node.consul
domain to access nodes. You have some options for resolving the domain:
- Make the domain accessible using the consul local DNS
- Use your own hostnames as the full name of the node
- Or, suffix the
COMPREPLY
variable with an existing domain (hack)
I have setup dnsmasq to forward the .consul
domain requests to the consul DNS server. And, added domain search resolution here:
# /etc/resolvconf/resolv.conf.d/base
search node.consul
This will attempt to resolve hostnames matching the name of the consul node, for example: ssh dev-web
would attempt to resolve the hostname {dev-web}.node.consul
through the local DNS server.
Putting It All Together
After saving the completion config, we'll need to reinitialize our bash session for it to take effect. Now our ssh
command will provide immediate and accurate insights into what instances are available.
someone@example:~$ ssh dev-
dev-foo-20 dev-foo-21 dev-mysql dev-web
Gentoo has provided some excellent documentation for bash completions here. You can write some pretty advanced completion scripts, if one were so inclined.
I highly recommend managing your Consul implementation with Ansible, or a similar configuration management tool. If you have questions or suggestions, please feel free to get in touch via the appropriate social outlet.