#!/bin/zsh use bastion --group remote use config --group remote DEPENDENCIES+=(timeout ssh) ##################################################################### USAGE__options=' -n, --name session name to test -s, --connection-string explicit session host / ssh connection string to test -t, --maximum-timeout maximum connection timeout before failure (in seconds) -c, --command testing command; performs echo by default --use-bastion true / false override for bastion preference (default: session.name.bastion.preferred or "false") ' USAGE__description=' Tests whether you can connect to a particular session or host string. ' ##################################################################### MAIN() { local CONNECTION_STRING REMOTE_NAME local TIMEOUT_SECONDS=3 local COMMAND='echo OK &>/dev/null' local USE_BASTION while [[ $# -gt 0 ]] do local _S=1 case $1 in -n | --name ) ((_S+=1)) REMOTE_NAME=$2 CONNECTION_STRING=$(remote.config.get-connection-string ${REMOTE_NAME}) ;; -s | --connection_string ) ((_S+=1)) CONNECTION_STRING="$2" ;; -t | --maximum-timout ) ((_S+=1)) TIMEOUT_SECONDS=$2 [[ ${TIMEOUT_SECONDS} -gt 0 ]] \ || echo.error "invalid timeout seconds '${TIMEOUT_SECONDS}'" ;; -c | --command ) ((_S+=1)) COMMAND=$2 ;; --use-bastion ) ((_S+=1)) USE_BASTION=$2 case ${USE_BASTION} in true | false ) ;; * ) echo.error "invalid setting for '--use-bastion' (must be 'true' or 'false')" ;; esac ;; * ) echo.error "unrecognized argument '$1'" ;; esac [[ $_S -le $# ]] \ && shift $_S \ || echo.error "missing argument for '$1'" \ || shift $# done [ ${CONNECTION_STRING} ] \ || echo.error "unable to determine connection string" [ ${USE_BASTION} ] || { USE_BASTION=$(\ remote.config.query-connection-with-fallback \ ".sessions.${REMOTE_NAME}.bastion.preferred" \ 'false' \ ; ) } utils.check-errors --fail ########################################## local BASTION_HOST [[ ${USE_BASTION} =~ true ]] && { BASTION_HOST=$(\ remote.config.query-connection-with-fallback \ ".sessions.${REMOTE_NAME}.bastion.session" \ ) } [[ ${USE_BASTION} =~ true ]] && { [ ${BASTION_HOST} ] \ || echo.error "cannot connect to ${REMOTE_NAME}; no configured bastion host" \ || return 1 } case ${CONNECTION_STRING} in ( localhost | $USER@localhost ) CONNECTION_TEST() { return 0; } # current user on local machine can always connect ;; * ) case ${USE_BASTION} in ( true ) CONNECTION_TEST() { echo.debug "MAIN -n ${BASTION_HOST} -c \"$(remote.bastion.get-passthrough-prefix) remote test -- -n ${REMOTE_NAME} -c \"${COMMAND}\"\"" BASTION_TARGET="${REMOTE_NAME}" MAIN -n ${BASTION_HOST} -c "$(remote.bastion.get-passthrough-prefix) remote test -- -n ${REMOTE_NAME} -c \"${COMMAND}\"" return $? } ;; ( * ) CONNECTION_TEST() { local REMOTE_ARGS=() REMOTE_ARGS+=($(remote.config.get-ssh-args --type ssh ${REMOTE_NAME})) REMOTE_ARGS+=(-o BatchMode=yes) echo.debug "attempting\ntimeout ${TIMEOUT_SECONDS} ssh ${REMOTE_ARGS} ${CONNECTION_STRING} "'\'"\"${COMMAND}"'\'"\"" >&2 timeout --foreground ${TIMEOUT_SECONDS} ssh ${REMOTE_ARGS[@]} "${CONNECTION_STRING}" "${COMMAND}" >&2 } ;; esac ;; esac [ ${REMOTE_NAME} ] && { [[ $(remote.config.query-connection .sessions.${REMOTE_NAME}.enabled) =~ false ]] && { echo.error "automatic connection to ${REMOTE_NAME} is disabled" return 1 } } [ ${REMOTE_NAME} ] || REMOTE_NAME=explicit echo.status "testing connection ${CONNECTION_STRING} (${REMOTE_NAME}$([ ${BASTION_TARGET} ] && echo " -> ${BASTION_TARGET}"))" \ && CONNECTION_TEST \ && echo.success "successfully connected to '${CONNECTION_STRING}' (${REMOTE_NAME})" \ || echo.error "connection to '${CONNECTION_STRING} (${REMOTE_NAME})' failed" \ }