GCP SSH autocomplete with FZF and ZSH

I got inspired by AffectV post on AWS SSH completion and decided to write something similar for GCP.

Here’s the end result:
asciicast

Prerequisites #

Writing the module #

First, we need to figure out how to get the list of information we want. gcloud provides a somewhat sensible formatting and filtering options for that, e.g. if I want to get the name, zone and external IP of the instance in a flat tab-separated table, we’ll have to set options like so:

λ gcloud compute instances list \
                         --format="table[no-heading](name,zone,status,networkInterfaces[0].accessConfigs[0].natIP)" \
                         --filter="status=RUNNING"
my-web-v37r              australia-southeast1-c  RUNNING  XX.XXX.XX.XX
my-web-wp2d              australia-southeast1-c  RUNNING  XX.XXX.XX.XX
my-worker-b96q           australia-southeast1-c  RUNNING  XX.XXX.XX.XX

Perfect.

Note: if you want to add more information to your default output, you can use gcloud compute instances list --format=flattened to figure out what fields you need.

Now, let’s create a dummy ssh function so we don’t mess with default autocomplete and don’t put a latency penalty on autocompleting the regular ssh command:

gssh()
{
  ssh $@
}

Next, we’ll need to write a FZF completion module. For that, we’ll need to write a function _fzf_complete_$YOURCOMMAND, following one of FZF examples, e.g.:

_fzf_complete_gssh() {
  ARGS="$@"
  local machines
  machines=$(gcloud compute instances list \
             --format="table[no-heading](name,zone,status,networkInterfaces[0].accessConfigs[0].natIP)" \
             --filter="status=RUNNING")
  if [[ $ARGS == 'gssh '* ]]; then
    _fzf_complete "--reverse --multi" "$@" < <(
        echo $machines
    )
  else
    eval "zle ${fzf_default_completion:-expand-or-complete}"
  fi
}

Then, we need to tell FZF what to pass back to the executable once completion has been selected. In our case, we want the instance’s external IP, so we select the 4th column using awk:

_fzf_complete_gssh_post() {
    awk '{print $4}'
}

Putting it all together #

Here’s how our module looks like:

gssh()
{
  ssh $@
}

_fzf_complete_gssh() {
  ARGS="$@"
  local machines
  machines=$(gcloud compute instances list \
             --format="table[no-heading](name,zone,status,networkInterfaces[0].accessConfigs[0].natIP)" \
             --filter="status=RUNNING")
  if [[ $ARGS == 'gssh '* ]]; then
    _fzf_complete "--reverse --multi" "$@" < <(
        echo $machines
    )
  else
    eval "zle ${fzf_default_completion:-expand-or-complete}"
  fi
}

_fzf_complete_gssh_post() {
    awk '{print $4}'
}

The only thing we need to do is to paste it into our .zshrc (or include it using your favourite plugin manager) and we’re good to go.

Just type gssh **, and then press TAB.

 
0
Kudos
 
0
Kudos

Now read this

Ansible - Bootstrap python

When you’re starting hosts from scratch, you can avoid preinstalling python in your image or startup scripts by issuing a raw command in ansible in a pre_tasks section. This doesn’t require python to be available on the remote host,... Continue →