使用Serverspec进行Docker映像构建测试,检查空gems缓存失败

我目前遇到的问题是通过Serverspec测试Docker镜像构建。 简而言之,我想要做的是确保在图像构建期间显式清除Ruby gems构建缓存,例如通过在Dockerfile中发出rm -rf /usr/lib/ruby/gems/*/cache/*.gem

我正在使用的Dockerfile骨架如下所示:

  # Dockerfile FROM alpine:3.7 RUN apk add --no-cache \ dumb-init \ ruby \ && apk add --no-cache --virtual .build-deps \ build-base \ ruby-dev RUN gem install --no-rdoc --no-ri json \ && gem install --no-rdoc --no-ri oj RUN apk del .build-deps \ && rm -rf /var/cache/apk/* \ /tmp/* /var/tmp/* 

在添加gem cache删除命令之前,我实现了以下Serverspec测试,以便能够从失败的测试开始:

  # ./spec/Dockerfile_spec.rb describe "Dockerfile" do before(:all) do @image = Docker::Image.build_from_dir('.') @image.tag(repo: 'demo', tag: 'latest') set :os, family: :alpine set :backend, :docker set :docker_image, @image.id end it "removes build dependencies during cleanup" do # Some more assertions # ... expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to eq 0 end end 

假设上面的Dockerfile,测试运行为绿色:

  $ bundle exec rspec spec/Dockerfile_spec.rb .... Finished in 3.95 seconds (files took 0.24091 seconds to load) 4 examples, 0 failures 

但是,这不应该是这种情况,因为gem缓存尚未被清除,因此不是空的,即我期望相应的断言在测试执行期间失败。

通过从新构建的映像启动容器并检查gems缓存目录,可以轻松validation这一点:

  $ docker run --rm -it demo:latest sh / # ls /usr/lib/ruby/gems/2.4.0/cache/ json-2.1.0.gem oj-3.4.0.gem 

测试相反并期望非空目录,执行失败并显示错误消息:

  # ./spec/Dockerfile.rb it "removes build dependencies during cleanup" do # Some more assertions # ... expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0 end # Command line $ bundle exec rspec spec/Dockerfile_spec.rb .F.. Failures: 1) Dockerfile removes build dependencies during cleanup Failure/Error: expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0 expected: value != 0 got: 0 (compared using ==) 

显然, Dir.glob()命令不会返回gems缓存目录中正确数量的文件。

有趣的是,在容器内手动运行Dir.glob()命令会返回预期的结果:

  $ docker run --rm -it demo:latest sh / # apk add --no-cache ruby-irb ... / # irb irb(main):001:0> Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size => 2 

首先,这让我认为测试没有在容器内正确执行,而是在主机上执行,但是进一步的实验无法证实这一点。

你有什么想法吗? 这可能是Serverspec / Rspec问题吗?

谢谢!

编辑

首先,这让我认为测试没有在容器内正确执行,而是在主机上执行,但是进一步的实验无法证实这一点。

我终于发现这个假设是错误的。 实际上,无论出于何种原因, Dir.glob()调用都在容器外执行。

我终于弄清楚出了什么问题,实际上它非常简单:

假设我们有一个像这样的Serverspec测试用例:

 it "removes build dependencies during cleanup" do # Some more assertions # ... expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0 end 

现在执行期间发生的事情是在调用expect()例程之前评估Dir.glob()调用。 因此,正如我在post中已经指出的那样, Dir.glob()命令在容器的作用域之外运行,并检查相应的主机目录。 如果您想检查容器文件夹是否为空,您可以按如下方式实现:

 it "clears gem/apk caches as well as tmp files/dirs" do expect(command('ls /usr/lib/ruby/gems/*/cache/*.gem | wc -l').stdout).to eq "0\n" end 

不可否认,这可能不是最美丽和优雅的解决方案,但基本上可以胜任。 如果您有其他想法和建议,请随时发布。

    Interesting Posts