为什么在没有——interactive的情况下运行docker容器会失败?



当我在docker容器内运行expect脚本时,我得到了一个错误。

首先,我创建了一个简单的期望脚本test.expect

#!/usr/bin/expect -f 
set timeout 3
#expect_before timeout { puts "nTimeout fired"; exit 1 }
puts "--- Start"
lassign $argv name duration
spawn bash -c "echo Hello ${name} && sleep ${duration} "
expect {
eof {
puts "No timeout";
}
}
puts "--- End"

注意:expect_before首先被注释掉

现在docker镜像Dockerfile

FROM alpine:latest
RUN apk update && 
apk upgrade && 
apk add bash expect
COPY test.expect /root
ENV PATH $PATH:/root
WORKDIR /root
ENTRYPOINT ["test.expect"]

现在构建并运行docker:

$ docker build -t test-expect .
$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
--- End
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
--- End

无论是否使用docker-i(--interactive),脚本中的两个路径都可以正常工作。现在取消expect_before的注释。重建并重新运行:

$ docker build -t test-expect .
$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)

自然地,当我第一次在我的实际管道(GitHub工作流,调用动作,运行大型容器,与复杂的脚本,需要我的实际期望脚本)中看到这一点时,我认为这与期望在没有标准输入和标准输出的环境中运行的事实有关。直到我最终创建了这个简单得多的测试用例,我才找到了错误的实际原因。

我无法理解为什么expect_before会导致这种行为,更不用说我能做些什么来修复它了。

运行expect-d没有给我任何洞察力

$ docker run test-expect Martin 5
expect version 5.45.4
argv[0] = /usr/bin/expect  argv[1] = -df  argv[2] = /root/test.expect  argv[3] = Martin  argv[4] = 5
set argc 2
set argv0 "/root/test.expect"
set argv "Martin 5"
executing commands from command file /root/test.expect
--- Start
spawn bash -c echo Hello Martin && sleep 5
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {7}
expect: read eof
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) ""
error writing "stdout": bad file number
while executing
"puts "--- End""
(file "/root/test.expect" line 15)

我偶然发现了真正的答案!

您可以使用expect_beforeexpect_after,而不是在程序中的每个expect命令中添加所有替代选项。这些允许您设置替代序列,除了您显式地写入expect命令中的序列之外,当前生成的进程中的每个后续expect命令将对这些序列作出反应。如果从生成的命令返回的序列匹配多个备选项,则优先级顺序为

expect_before
expect
expect_after

现在-一个警告。你应该指定任何expect_beforeexpect_after命令。Expect可以控制多个生成的进程,如果你在错误的地方指定了_befores和_afters,你最终可能会将它们应用到错误的进程。

如果您根本没有生成进程,并且expect_before您可能会得到不稳定的结果-例如,一个应用程序交互运行,但从crontab失败或者在操作系统的一个版本上运行,但在下一个版本上失败。如果你没有意识到风险,很难识别这个问题!

所以修复非常简单:

#!/usr/bin/expect -f 
set timeout 3
puts "--- Start"
lassign $argv name duration
spawn bash -c "echo Hello ${name} && sleep ${duration} "
expect_before timeout { puts "nTimeout fired"; exit 1 }
expect {
eof {
puts "No timeout";
}
}
puts "--- End"

现在无论-i标志如何,它都可以工作

$ docker run -i test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run -i test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired
$ docker run test-expect Martin 2
--- Start
spawn bash -c echo Hello Martin && sleep 2
Hello Martin
No timeout
--- End
$ docker run test-expect Martin 5
--- Start
spawn bash -c echo Hello Martin && sleep 5
Hello Martin
Timeout fired

相关内容

  • 没有找到相关文章

最新更新