Как использовать классы с параметрами Puppet для принудительного упорядочения применяемых ресурсов?

Документы Puppetlabs заявляют, что для того, чтобы один класс требовал другого класса, вы должны использовать синтаксис цепочки отношений и объявить оба класса в своих внешних узлах.

У меня есть класс repo, который создает определение репо yum, от которого зависят многие пакеты в каждом модуле. В каждом модуле у меня есть оператор класса ['repo'] → Class ['modulename'], и оба класса объявляются в node. Однако, когда марионетки работают, он не всегда выполняет класс репо перед классом модуля, как и ожидалось. Почему нет? Пример ниже (марионетка 2.6.16):

EDIT: Похоже, что есть три основных решения этой проблемы.

  • Замените зависимости классов на зависимости ресурсов, используя перед/требуют метапараметры (как показано в ответе turingmachine).
  • Удалить внешние зависимости классов и явно определять зависимости между внутренними классами.
  • Используйте тип привязки, предоставляемый Puppetlabs в модуле stdlib, чтобы содержат класс, позволяющий зависящему классу создавать ссылку к внешнему классу с использованием синтаксиса цепочки.

Итак, какой из этих подходов лучше всего, учитывая Puppet v3 и желание продолжать рефакторинг с минимальным продвижением вперед?

Манифест puppettest.pp:

class { 'repo': }
class { 'maradns': }

class repo {
  class { 'repo::custom': }
}

class repo::custom {
  yumrepo {'custom':
    enabled  => 1,
    gpgcheck => 0,
    descr    => "Local respository - ${::architecture}",
    baseurl  => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
  }
}

class maradns {
  Class['repo'] -> Class['maradns::install']
  Class['maradns::install'] -> Class['maradns::config']
  Class['maradns::config'] ~> Class['maradns::service']
  class { 'maradns::install': }
  class { 'maradns::config':  }
  class { 'maradns::service': }
}

class maradns::install {
  package { 'maradns':
    ensure  => present,
  }
}

class maradns::config {
  file { 'mararc':
    ensure  => present,
    path    => '/etc/mararc',
    mode    => '0644',
    owner   => root,
    group   => root,
  }
}

class maradns::service {
  service { 'maradns':
    ensure     => running,
    enable     => true,
    hasrestart => true,
  }
}

Вывод:

puppet apply puppettest.pp    
err: /Stage[main]/Maradns::Install/Package[maradns]/ensure: change from absent to present failed: Execution of '/usr/bin/yum -d 0 -e 0 -y install maradns' returned 1: Error: Nothing to do

notice: /Stage[main]/Maradns::Config/File[mararc]: Dependency Package[maradns] has failures: true
warning: /Stage[main]/Maradns::Config/File[mararc]: Skipping because of failed dependencies
notice: /Stage[main]/Maradns::Service/Service[maradns]: Dependency Package[maradns] has failures: true
warning: /Stage[main]/Maradns::Service/Service[maradns]: Skipping because of failed dependencies
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/descr: descr changed '' to 'Local respository - x86_64'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/baseurl: baseurl changed '' to 'http://repo.test.com/CentOS/\$releasever/\$basearch'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/enabled: enabled changed '' to '1'
notice: /Stage[main]/Repo::Custom/Yumrepo[custom]/gpgcheck: gpgcheck changed '' to '0'
notice: Finished catalog run in 2.15 seconds

Ответы

Ответ 1

Хорошей отправной точкой для отладки проблем с зависимостями является указание марионетке генерировать граф зависимостей.

puppet apply --graph --noop manifest.pp
dot -Tpng /var/lib/puppet/state/graphs/relationships.dot -o relationships.png

Сделав это, вы увидите, что класс repo:custom вообще не имеет информации о зависимости.

maradns::install уверен, что имеет зависимость от класса repo, но не от класса repo::custom, потому что repo::custom не имеет зависимости от repo.

Синтаксис объявления нового класса class {'classname':} не устанавливает никаких зависимостей, он ведет себя так же, как синтаксис include classname.

Таким образом, либо вы устанавливаете зависимость от repo::custom до repo, либо инструктируете класс maradns::install напрямую зависеть от класса repo:custom.

Но у вас будет больше проблем. Зависимость от класса будет только убедиться, что этот класс применяется. Тем не менее, не будет никаких зависимостей, установленных для хранения ресурсов.

Я бы смоделировал ваше дело следующим образом:

class { 'repo:custom': }
class { 'maradns': }

class repo {
}

class repo::custom {
  yumrepo {'custom':
    enabled  => 1,
    gpgcheck => 0,
    descr    => "Local respository - ${::architecture}",
    baseurl  => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
  }
}

class maradns {
  class{[
    'maradns::package',
    'maradns::config',
    'maradns::service',
  ]:}
}

class maradns::package {
  package { 'maradns':
    ensure  => present,
    require => Yumrepo['custom'],
  }
}

class maradns::config {
  file { 'marac:config':
    ensure  => present,
    mode    => '0644',
    owner   => root,
    group   => root,
  }
}

class maradns::service {
  service { 'maradns':
    ensure     => running,
    enable     => true,
    hasrestart => true,
    require => [
      Package['maradns'],
      File['mararc:config'],
    ],
  }
}

Ответ 2

Из макет куклы stdlib

В Puppet 2.6, когда класс объявляет другой класс, ресурсы в внутренний класс не содержится внешним классом. Эта плохо взаимодействует с шаблоном составления сложных модулей из меньшие классы, поскольку это делает невозможным конечные пользователи указать отношения между внешним классом и другими модулями.

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

Основываясь на опубликованном манифесте, примером может быть:

Манифест puppettest.pp:

class { 'repo': }
class { 'maradns': }

class repo {
  anchor { 'repo::begin': } ->
  class { 'repo::custom': } ->
  anchor { 'repo::end': }
}

class repo::custom {
  yumrepo {'custom':
    enabled  => 1,
    gpgcheck => 0,
    descr    => "Local respository - ${::architecture}",
    baseurl  => 'http://repo.nike.local/CentOS/\$releasever/\$basearch';
  }
}

class maradns {
  Class['repo'] -> Class['maradns::install']
  Class['maradns::install'] -> Class['maradns::config']
  Class['maradns::config'] ~> Class['maradns::service']
  class { 'maradns::install': }
  class { 'maradns::config':  }
  class { 'maradns::service': }
}

class maradns::install {
  package { 'maradns':
    ensure  => present,
  }
}

class maradns::config {
  file { 'mararc':
    ensure  => present,
    path    => '/etc/mararc',
    mode    => '0644',
    owner   => root,
    group   => root,
  }
}

class maradns::service {
  service { 'maradns':
    ensure     => running,
    enable     => true,
    hasrestart => true,
  }
}

Ответ 3

Рассматривали ли вы этапы запуска в качестве альтернативного механизма? На этапе запуска вы можете связать класс со "сценой". По умолчанию все происходит на этапе main. Но вы можете настроить этап, который встречается перед основным, а затем связать этот класс с этим "до основного" этапа.

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

Более сложные сценарии - это когда новый кукольный сервер случайно достает пакет, прежде чем вы даже заявите о своем репо, и он получает его от современных публичных зеркал; то установлено ваше ремаркетирование (и, предположительно, теперь вы удалите общие зеркала). Так как эта машина "застряла" в новом артефакте, ситуации с азартными играм могут легко возникнуть, когда слишком новый пакет останавливает пакеты, которые вам не нужны, поскольку этот пакет не будет установлен, потому что он уже установлен слишком -Новая версия и многие менеджеры пакетов не будут понижать для вас; вы должны вручную вмешаться. Эта ситуация по существу требует ручной отладки для исправления; просто исправление правил марионетки недостаточно, потому что ущерб нанесен.

Итак, просто сопоставьте все определения репо с предыдущей фазой и сделайте с ним. Остановите отслеживание зависимостей ваших пакетов с репозиториями и дыханием.

Ответ 4

Что вы получаете, включив repo:: custom в репо, а не напрямую в зависимости от repo:: custom?

Образец объявления классов внутри таких классов также может быть настроен для дублирования определений. Я бы сосредоточился на использовании repo:: custom напрямую, если это возможно.