Ошибка проверки пустого кеша драгоценных камней с использованием Serverspec для тестирования сборки образа Docker

В настоящее время у меня проблема с тестированием сборок образов Docker с помощью Serverspec. В двух словах, я хочу убедиться, что во время сборки образа кеш сборки Ruby gems очищается явно, например. выпустив rm -rf /usr/lib/ruby/gems/*/cache/*.gem в Dockerfile.

Скелет 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/*

Перед добавлением команды удаления кеша гемов я реализовал следующий тест 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

Однако этого не должно быть, поскольку кеш гемов еще не очищен и, следовательно, не пуст, т.е. я ожидаю, что соответствующее утверждение не будет выполнено во время выполнения теста.

В этом легко убедиться, запустив контейнер из только что созданного образа и проверив каталог кеша gems:

 $ 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() по какой-то причине выполняются вне контейнера.


person pklndnst    schedule 06.02.2018    source источник


Ответы (1)


Я наконец понял, что пошло не так, и на самом деле это довольно просто:

Предположим, что у нас есть один тестовый пример 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

Теперь во время выполнения происходит то, что вызов Dir.glob() оценивается до вызова подпрограммы expect(). Следовательно, как я уже указывал в своем посте, команда 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

По общему признанию, это, вероятно, не самое красивое и элегантное решение, но в основном оно работает. Если у вас есть другие идеи и предложения, не стесняйтесь публиковать их.

person pklndnst    schedule 13.02.2018