Проверить наличие внутреннего класса с помощью RSpec и PDK

У меня есть довольно простой модуль марионеток для веб-сервиса под управлением tomcat. Я хочу настроить logrotate на Tomcatcatalina.out файл, и я хочу начать с написания теста, подтверждающего, что logrotate включен в модуль и настроен с правильными настройками.

Вот урезанная версия моего webservice.pp, например:

class my_module::webservice (
  ...
){
  include ::tomcat_server

  ...

  logrotate::rule { 'tomcat':
    path          => '/var/log/tomcat/catalina.out',
    rotate        => 1,
    rotate_every  => 'day',
    copytruncate  => true,
    missingok     => true,
    compress      => true,
    delaycompress => true,
  }
}

и я включил модуль logrotate forge в свой .fixtures.yml вот так:

fixtures:
  forge_modules:
    logrotate:
      repo: 'puppet-logrotate'
      ref:  '3.2.1'
    ...

Но я могу написать только тест, подтверждающий, что logrotate входит в модуль так:

require 'spec_helper'

describe 'my_module::webservice' do
  on_supported_os.each do |os, os_facts|
    context "on #{os}" do
      let(:facts) { os_facts }

      it { is_expected.to compile }

      it { is_expected.to contain_class('logrotate') }
    end
  end
end

Это не сработает (если я удалю блок logrotate из init.pp тогда тесты еще проходят):

it { is_expected.to contain_class('logrotate::conf') }

и не просит with:

it { is_expected.to contain_class('logrotate') \
  .with('path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
  )
}

а также отдельный / вложенный describe блок:

describe 'logrotate::rule' do
  let(:title) { 'tomcat' }
  let(:params) do
    {
        'path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
    }
  end
end

Я не могу найти ничего в документах rspec, в которых упоминается что-либо, кроме проверки определения класса. Возможно ли вообще то, что я пытаюсь сделать?

Вот мой макет каталога:

puppet
  `- modules
        `- my_module
             |- data
             |- manifests
             |    |- init.pp
             |    `- webservice.pp
             |- spec
             |    |- classes
             |    |    `- webservice_spec.rb
             |    `- spec_helper.rb
             |- .fixtures.yml
             |- Gemfile
             |- hiera.yaml
             |- metadata.json
             `- Rakefile

1 ответ

Решение

У меня есть довольно простой модуль марионеток для веб-сервиса под управлением tomcat. Я хочу настроить logrotate в файле Tomcat catalina.out, и я хочу начать с написания теста, подтверждающего, что logrotate включен в модуль и настроен с правильными настройками.

Звучит очень разумно. Однако это...

Вот урезанная версия моего init.pp, например:

class my_module::webservice (
  ...
){

... в лучшем случае плохая практика. Если он вообще существует, тоinit.pp манифест модуля my_module должен определять только класс my_module. Класс с именемmy_module::webservice вместо этого следует определить в манифесте с именем webservice.pp в модуле my_module. Требования к макету модуля задокументированы в онлайн-документации Puppet. Хотя вам, возможно, удастся избежать некоторых расхождений с этими спецификациями, в этом есть только обратная сторона.

На этом этапе я заметил, что "внутренний класс" - это не идиоматическая терминология Марионетки, и он предполагает неправильное понимание того, с чем вы работаете. Конкретно это...

logrotate::rule { 'tomcat':

[...]

... не объявляет класс вообще, а объявляет ресурс типаlogrotate::rule, который, по-видимому, является определенным типом, предоставляемым модулем puppet/logrotate. В общем, объявление ресурса ничего не говорит о классах из модуля (если таковой имеется), который предоставляет тип ресурса.

Кроме того, хотя вполне возможно, что объявление logrotate::rule ресурс действительно вызывает класс logrotate для включения в каталог, это будет деталью реализации logrotate::rule, и поэтому ваши спецификации не должны проверять его. Только еслиmy_module::webservice ожидается, что сам объявит класс logrotate если его тесты проверяют это.

Вы продолжаете говорить:

Это не сработает (если я удалю блок logrotate из init.pp, тесты все равно пройдут):

it { is_expected.to contain_class('logrotate::conf') }

Вы не представили нам достаточно кода, чтобы определить, почему тесты проходят, когда он включен в них, но что-то очень странно, если это ожидание когда-либо оправдывается. logrotate::confтакже является определенным (ресурсным) типом, а не классом, поэтому ожидание никогда не должно быть успешным. И следуя теме, которую я представил выше, если classmy_module::webservice не заявляет никаких logrotate::conf ресурс напрямую, то его тесты не должны проверять его.

и не просит с:

it { is_expected.to contain_class('logrotate') \
  .with('path'          => '/var/log/tomcat/catalina.out',
        'rotate'        => 1,
        'rotate_every'  => 'day',
        'copytruncate'  => true,
        'missingok'     => true,
        'compress'      => true,
        'delaycompress' => true,
  )
}

Конечно, это не удается. Он выражает ожидание объявления классаlogrotate, но на самом деле вы объявили ресурс типа logrotate::rule. Даже еслиlogrotate::rule объявил logrotate, нельзя было ожидать, что он будет передавать свой собственный список параметров.

а также отдельный / вложенный блок описания:

describe 'logrotate::rule' do

[...]

Опять же, это не удивительно. Такойdescribe блок сообщает RSpec, что logrotate::ruleэто тестируемый класс. Мало того, что это не тестируемый класс (это, конечно,my_module::webservice), но, опять же, logrotate::ruleэто вообще не класс. RSpec, безусловно, может тестировать и определенные типы, но это не то, что вам нужно.

Чтобы проверить, объявлен ли ресурс тестируемым классом, используется предикат формыcontain_тип(заглавие), где любые разделители пространства имен (::) в названии типа заменяются двойным подчеркиванием. Например:

it do
  is_expected.to contain_logrotate__rule('tomcat')
end

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

require 'spec_helper'

describe 'my_module::webservice' do
  on_supported_os.each do |os, os_facts|
    context "on #{os}" do
      let(:facts) { os_facts }

      it do
        is_expected.to compile
        is_expected.to contain_logrotate__rule('tomcat')
          .with(
            path: '/var/log/tomcat/catalina.out',
            rotate: 1,
            rotate_every: 'day',
            copytruncate: true,
            missingok: true,
            compress: true,
            delaycompress: true
          )
      end
    end
  end
end

Кстати, обратите внимание, что когда вы хотите протестировать несколько предикатов на одном примере, гораздо эффективнее сгруппировать их вместе в одном it блок, как показано выше, чем помещать каждый в свой itблок. То есть вы, вероятно, заметите разницу во времени выполнения теста даже при объединении всего двухit блоки в один.

Кроме того, мой пример выше демонстрирует стиль кодирования, близкий к тому, который требуется, чтобы избежать предупреждений от pdk validate, что подводит нас к дополнительному пункту: всегда полезно проверить, что pdk validateзавершается без ошибок или предупреждений перед попыткой модульных тестов. Вы, вероятно, обнаружите, что он чрезмерно требователен к стилям кода Puppet и Ruby, но он также обнаружит некоторые проблемы, которые приводят к загадочным ошибкам тестирования. Кроме того, он работает намного быстрее, чем тесты, и улавливает практически все синтаксические ошибки как в Puppet, так и в коде Ruby. Очень неприятно, что ваши тесты долго терпят неудачу из-за незначительной синтаксической ошибки.

Другие вопросы по тегам