通过环境传递bash代码(用于docker-compose)

我试图处理docker-compose命令的一个已知问题,因此我以一个简单的解决scheme结束了脚本,检查脚本是否准备好启动命令。

这是这个想法,有一些环境variables定义到容器:

  • WAIT_COMMAND应该是一个在sh脚本中定义一个逻辑testing的string,它必须返回一个布尔值
  • START_CMD根据WAIT_COOMAND结果运行命令的string,否则请重试,直到到达LOOPS
  • LOOPS多less次尝试
  • 睡眠之间多less次睡眠

在那个例子中,我正在等待elasticSearch在启动我的应用程序之前作出响应,这取决于ES数据的启动。

以下脚本将是我在docker集装箱上的入口点。

导出ENVvariables

WAIT_COMMAND="$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) == 200" LOOPS=3 START_CMD="python my_script_depending_on_elastic.py" SLEEP=2 

具有上述ENVvariables可用的脚本应等到ES请求返回代码200

 #!/bin/bash is_ready() { eval $WAIT_COMMAND } # wait until is ready i=0 while ! is_ready; do i=`expr $i + 1` if [ $i -ge $LOOPS ]; then echo "$(date) - still not ready, giving up" exit 1 fi echo "$(date) - waiting to be ready" sleep $SLEEP done #start the script exec $START_CMD 

问题是代码在is_ready函数行中不起作用 ,它不返回布尔值,而是试图执行200作为命令

 # ./wait_elastic.sh ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:43 UTC 2015 - waiting to be ready ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:45 UTC 2015 - waiting to be ready ./wait_elastic.sh: line 9: 200: command not found Fri Jul 3 18:26:47 UTC 2015 - still not ready, giving up 

我该如何做testing来检查curl响应是否正常?

如何指定一个逻辑testing命令为:

 WAIT_COMMAND="$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) == 200" 

以及如何评估它:

 is_ready() { eval $WAIT_COMMAND } 

这个答案是不好的做法。 请考虑一个不涉及eval的方法。

 wait_command='[ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]' is_ready() { eval "$wait_command" } 

与原始代码的差异:

  • 其实包括test命令同义词[在被评估的环境variables(之前的版本不是合法的bash比较语法)内部)。
  • == (不是POSIX [ ]中的有效运算符)切换到= (POSIX允许)。 请参阅test命令的标准文档 。
  • 在分配时从双引号切换到单引号,这样curl不会运行,直到调用eval
  • 引用传递给eval的内容,以防止在代码到达eval命令之前发生string拆分和glob扩展。

docker-compose的上下文中实现它可能如下所示:

 app: command: docker/wait environment: - wait_command=[ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ] - wait_loops=10 - wait_sleep=30 

…用脚本如下所示:

 #!/bin/bash s_ready() { eval "$wait_command"; } # wait until is ready i=0 while ! is_ready; do if (( ++i >= wait_loops )); then echo "$(date) - still not ready, giving up" exit 1 fi echo "$(date) - waiting to be ready" sleep $wait_sleep done 

如果你想存储代码,不要使用标量variables – 使用函数; 这就是他们的目的。

如果你想通过一个环境variables传递代码,那么答案是一个导出的函数:

 wait_command() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ] } export -f wait_command 

…创build一个如下所示的环境variables(在现代的官方shellshock补丁bash中):

 BASH_FUNC_wait_command%%=() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ] 

…你可以像这样运行,没有eval

 wait_command 

再次重申,更清楚一点: 导出的函数只是具有特殊名称和值的环境variables 。 如果您创build一个名称以BASH_FUNC_并以%%结尾且其值以() {开头的环境variables,则您刚刚创build了一个导出函数,该函数将可用于在环境中随其启动的shell中运行。


因此,在你的.yml文件中,你可以使用像这样的东西:

 environment: {BASH_FUNC_wait_command%%: '() { [ $(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st) = 200 ]'} 

不要使用eval 。 相反,只是得到状态

 status=$(curl --write-out %{http_code} --silent --output /dev/null http://elastic:9200/_cat/health?h=st)" 

然后直接进行testing。

 is_ready () { [[ $status -eq 200 ]] }