我目前正在测试bash脚本以执行数据库迁移。
这些脚本基本上接受一些参数,例如:
在脚本中,有一个函数可以“构建”要执行的mysql和mysqldump命令,具体取决于发件人/目的地服务器是本地服务器还是远程服务器。
函数build_mysql_command
的用法如下:
_query="$(build_mysql_command mysql from)"
_dump="$(build_mysql_command mysqldump from)"
_restore="$(build_mysql_command mysql to)"
但是,当函数build_mysql_command
必须调用open_ssh_tunnel
时,它会挂在最后一条指令上,正如我通过将脚本与-x
开关一起使用所测试的那样。
相反,如果我将SSH隧道开口放在build_mysql_command
外面,然后从那里删除呼叫,那么它就可以了。
但是,我不认为我在上述功能上犯了任何错误,所以我不理解为什么脚本会挂起。
下面是一个非常简单的示例,它显示了问题,其中我用1.2.3.4
替换了远程服务器的实际IP地址:
#!/bin/bash
set -x
set -o pipefail
# $1 = 'from' or 'to'
get_local_port() {
case "$1" in
from)
echo 30303
;;
to)
echo 31313
;;
*)
echo 0
;;
esac
}
# $1 = 'from' or 'to'
build_ssh_command() {
local _ssh="ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no"
if [ ! -z "${params[password-$1]}" ] ; then
_ssh="sshpass -f ${params[password-$1]} $_ssh"
fi
echo "$_ssh"
}
# $1 = 'from' or 'to'
open_ssh_tunnel() {
# se non già aperto
if [ -z "${cleanup[ssh-tunnel-$1]}" ] ; then
local _port="$(get_local_port "$1")"
local _ssh="$(build_ssh_command "$1")"
local _pregp="fnNTL $_port:localhost:3306 ${params[migrate-$1]}"
local _command="$_ssh -$_pregp"
# tento apertura tunnel SSH
if ! $_command ; then
return 1
else
# salvo PID del tunnel così aperto
local _pid="$(pgrep -f "$_pregp" 2> /dev/null)"
if [ -z "$_pid" ] ; then
return 1
fi
cleanup["ssh-tunnel-$1"]="$_pid"
fi
fi
return 0
}
# verifica se un indirizzo fa riferimento alla macchina locale
# $1 = indirizzo da verificare
is_local_address() {
local _host="$(hostname)"
case "$1" in
localhost|"127.0.0.1"|"$_host")
return 0
;;
*)
return 1
;;
esac
}
# costruisce un comando di dump o restore MySQL
# $1 = comando di base
# $2 = tipo server ('from' o 'to')
build_mysql_command() {
local _command="$1 --user=root --password=xxx"
if is_local_address "${params[migrate-$2]}" ; then
# connessione tramite socket
_command="$_command --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock"
elif open_ssh_tunnel "$2" ; then
# altrimenti uso connessione tramite tunnel SSH
_command="$_command --protocol=tcp --host=localhost --port=$(get_local_port "$2")"
else
_command=""
fi
echo "$_command"
}
# parametri di esecuzione dello script
declare -A params=(
["migrate-from"]="localhost"
["migrate-to"]="1.2.3.4"
)
_query="$(build_mysql_command "mysql" "from")"
echo "_query = $_query"
_dump="$(build_mysql_command "mysqldump" "to")"
echo "_dump = $_dump"
# fine test
这是运行时的输出:
+ set -o pipefail
+ params=(["migrate-from"]="localhost" ["migrate-to"]="1.2.3.4")
+ declare -A params
++ build_mysql_command mysql from
++ local '_command=mysql --user=root --password=xxx'
++ is_local_address localhost
+++ hostname
++ local _host=my.host.name
++ case "$1" in
++ return 0
++ _command='mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
++ echo 'mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
+ _query='mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
+ echo '_query = mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock'
_query = mysql --user=root --password=xxx --protocol=socket --socket=/opt/agews64/data/mysql/mysql.sock
++ build_mysql_command mysqldump to
++ local '_command=mysqldump --user=root --password=xxx'
++ is_local_address 1.2.3.4
+++ hostname
++ local _host=asp10.626suite-online.it
++ case "$1" in
++ return 1
++ open_ssh_tunnel to
++ '[' -z '' ']'
+++ get_local_port to
+++ case "$1" in
+++ echo 31313
++ local _port=31313
+++ build_ssh_command to
+++ local '_ssh=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
+++ '[' '!' -z '' ']'
+++ echo 'ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
++ local '_ssh=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no'
++ local '_pregp=fnNTL 31313:localhost:3306 1.2.3.4'
++ local '_command=ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -fnNTL 31313:localhost:3306 1.2.3.4'
++ ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -fnNTL 31313:localhost:3306 1.2.3.4
Warning: Permanently added '1.2.3.4' (ECDSA) to the list of known hosts.
+++ pgrep -f 'fnNTL 31313:localhost:3306 1.2.3.4'
++ local _pid=8919
++ '[' -z 8919 ']'
++ cleanup["ssh-tunnel-$1"]=8919
++ return 0
+++ get_local_port to
+++ case "$1" in
+++ echo 31313
++ _command='mysqldump --user=root --password=xxx --protocol=tcp --host=localhost --port=31313'
++ echo 'mysqldump --user=root --password=xxx --protocol=tcp --host=localhost --port=31313'
如您所见,该脚本在打开通往远程服务器的SSH隧道时挂在build_mysql_command
的最后一行,但是在构建本地命令时却没有问题。