xwp / wp-cli-ssh

[OBSOLETE] Seamlessly run WP-CLI commands on a remote server via SSH
157 stars 14 forks source link

Use SSH ControlPath to speed up connections #15

Open westonruter opened 10 years ago

westonruter commented 10 years ago

There is a configuration for SSH called ControlPath which keeps a single connection open across multiple SSH sessions, meaning subsequent connections re-use the initial connection and thus connect very fast. It would be great to make WP-CLI SSH use this somehow, where the initial SSH connection is not killed.

westonruter commented 10 years ago

A promising start. Even just caching the vagrant-ssh-config output causes a great speedup.

wp_cli_ssh_id=example.com; if [ ! -e /tmp/vagrant-ssh-config-$wp_cli_ssh_id ]; then vagrant ssh-config > /tmp/vagrant-ssh-config-$wp_cli_ssh_id; echo -e "ControlMaster auto\nControlPath /tmp/vagrant-ssh-controlpath-$wp_cli_ssh_id" >> /tmp/vagrant-ssh-config-$wp_cli_ssh_id; fi && if [ ! -e /tmp/vagrant-ssh-controlpath-$wp_cli_ssh_id ]; then ssh -q -F /tmp/vagrant_ssh_config default; fi; ssh -q %pseudotty% -F /tmp/vagrant_ssh_config default %cmd%

It should be backgrounding the ssh command inside of the conditional, however, but & seems to be illegal inside of an if statement.

dhbaird commented 10 years ago

I would like to see a speedup of the connection as well. I found a gist which looks related to this: https://gist.github.com/jedi4ever/5657094

dhbaird commented 10 years ago

When using ControlMaster, it looks like there isn't a need to cache the ssh-confg. Here's a script I came up with that is working okay. Does this help at all in your case, or do you find other caching and/or DNS tricks are also needed?

#!/bin/bash
# fastssh.sh
hostname=default
function fastssh() {
    if [ ! -e .vagrant-ssh ]; then
      vagrant ssh-config $hostname | ssh -t -t -F/dev/stdin -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' -f -N $hostname
    fi
    ssh -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' $hostname "$@"
}
fastssh "$@"
westonruter commented 9 years ago

@dhbaird thanks for that. What happens when the SSH connection gets cut? The .vagrant-ssh file will persist, but it won't work anymore right?

dhbaird commented 9 years ago

@westonruter yeah, you've got a good point there. If the ControlMaster gets killed, then it appears to leave a stale .vagrant-ssh, and the first attempt to connect will yield this message and remove .vagrant-ssh:

ssh: Could not resolve hostname default: Name or service not known

The second attempt to connect, because .vagrant-ssh is removed will succeed. So, here's a new script that retries exactly once. But I'm not sure what to do about it printing the extraneous "Could not resolve hostname..." message:

#!/bin/bash
# fastssh.sh
hostname=default
function fastssh() {
    if [[ ! -S .vagrant-ssh ]]; then
      vagrant ssh-config $hostname | ssh -t -t -F/dev/stdin -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' -f -N $hostname
    fi
    ssh -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' $hostname "$@"
    # if the attempt failed, retry exactly once:
    if [[ $? != 0 && ! -S .vagrant-ssh ]]; then
      vagrant ssh-config $hostname | ssh -t -t -F/dev/stdin -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' -f -N $hostname
      ssh -o 'ControlMaster auto' -o 'ControlPath .vagrant-ssh' $hostname "$@"
    fi
}
fastssh "$@"
westonruter commented 9 years ago

Could the PID for that initial connection be captured somehow? Then you could check to see if the PID is still valid in addition to whether the .vagrant-ssh is present.

dhbaird commented 9 years ago

Sorry for such a long wait, but here is a script that seems to be working very well. It automatically re-initializes the control channel if SSH got killed, and then maintains a backgrounded ssh for up to 120 minutes. It also gives you proper stdio and return code, just like normal ssh should:

fastssh() {
    [[ -e .vagrant_ssh_config ]] || vagrant ssh-config >.vagrant_ssh_config </dev/null
    ssh -F .vagrant_ssh_config \
        -o 'ControlMaster auto' \
        -o 'ControlPath .vagrant_ssh_control' \
        -o 'ControlPersist 120m' default "$@"
    return $?
}
dhbaird commented 9 years ago

...I think it still needs an detection to figure out when the ssh-config has gone stale, and needs to re-run vagrant ssh-config...

westonruter commented 9 years ago

Looking good!

lkraav commented 9 years ago

Could there be an incremental improvement opportunity here, based on let's say documentation on just how to use ~/.ssh/config to set ControlMaster in a global fashion? It looks like the above stuff tries to address "works for everybody everywhere" and it doesn't seem to be easy, based on the timeline here.

My motivation is getting wp ssh bash-completion as discussed in #21. Even though you could bash-complete also based on local installation and just live with possible configuration difference errors. But either way, just having multiple commands run quicker is a great win too.

lkraav commented 9 years ago

So this totally works, put it in your ~/.ssh/config

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600

Now mkdir ~/.ssh/sockets so it has a place to store the... well, sockets.

wp ssh runs very fast now. If the connection is anything half decent, should be quite usable for even remote bash-completion.