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

What’s making me happy 07-05-2017

Went hiking to Uloola Falls this weekend - was the first truly lengthy hike since my arrival in Australia, feels good to be back in the game. Mastodon is another distributed social network which seems to be taking off a bit better than... Continue →