Назад | Перейти на главную страницу

дизайн кода марионетки: как собрать аргументы в строку (избегая возможных повторяющихся ошибок объявления)

У меня проблемы с дизайном кода марионетки (версия 5.5). Я написал компонентный модуль для работы с ucarp. Он использует модуль eyp-systemd для регистрации службы ucarp в systemd. Теперь я хотел бы использовать модуль ucarp из двух независимых модулей профиля, которые управляют разными службами (в моем случае на самом деле haproxy и bind9). По сути это выглядит так:

class ucarp {
  systemd::service { 'ucarp':
    # list of parameters
  }
}

define ucarp::vip {
  # defines a virtual IP to be shared among several nodes
}

# ====================

class profile_haproxy {
  include ::ucarp
  ::ucarp::vip { 'haproxy': }
  # setup haproxy
}

# =====================

class profile_bind9 {
  include ::ucarp
  ::ucarp::vip { 'bind9': }
  # setup bind9
}

Это просто и хорошо работает.

Теперь актуальная проблема: лучше всего заказывать услугу ucarp после услуг, которые запускаются через ucarp. Это возможно с помощью параметра after:

class ucarp(
  Array[String] $after,
) {
  systemd::service { 'ucarp':
    after => $after.join(' '),
    # list of other parameters
  }
}

Это требует замены include ::ucarp по

class { '::ucarp':
  after => ['haproxy'],
}

или

class { '::ucarp':
  after => ['bind9'],
}

соответственно. Конечно, это сразу же привело бы к ошибке «повторяющееся объявление».

На самом деле я хотел бы иметь единственный экземпляр класса ucarp, который собирает все after параметры в одну строку, которую можно передать в systemd :: service. Как бы я это сделал?

На данный момент мне приходят в голову два возможных решения:

  1. Вилка eyp-systemdудалите параметр после и замените его определенным типом, например systemd::service::after который управляет соответствующей записью в файле определения службы. Я действительно не хочу этого делать. Обычно я уклоняюсь от модификации кузнечных модулей, так как это заставляет меня поддерживать их самостоятельно. В этом случае изменение тоже кажется довольно большим (включая изменение интерфейса).
  2. Представьте мой собственный определенный тип в модуле ucarp ucarp::order_after который ничего не делает. Модули профиля будут определять виртуальные экземпляры этого типа. Затем класс ucarp может использовать запрос puppetdb для сбора всех экземпляров ucarp::order_after. Большой недостаток здесь в том, что я имею дело только с виртуальными ресурсами, а не с экспортируемыми. Так что на самом деле нет необходимости задействовать puppetdb, что превращает этот подход в некрасивый обходной путь.

Следующее решение вдохновлено c4f4t0r:

  1. Представьте модуль профиля ucarp, единственная задача которого - создать экземпляр класса компонента ucarp с правильным after Сервисы. Список after услуги предоставляет hiera:
    class profile_ucarp (
      Array[String] $after,
    ) {
      class { '::ucarp':
        after => $after,
      }
    }
    profile_ucarp.after:
        - 'haproxy'
        - 'bind9'
    
    Другим классам профиля больше не нужно создавать экземпляр ucarp class - устранение потенциальных проблем с дублированием объявлений. Я считаю это решение лучше двух вышеупомянутых. Тем не менее, я не удовлетворен тем, что использование hiera для исправления проблемы, связанной исключительно с кодом, является неправильным использованием hiera.

Я надеюсь, что есть другие возможности, о которых я не могу сейчас думать.

Вам нужно использовать функцию contain и поместить параметры класса в hiera.

В модуле вашего профиля ваши классы profile :: haproxy и profile :: bind могут быть только одним, потому что ваши классы дублируются, потому что вы не используете hiera для хранения параметров класса

class profile::ucarp {
  contain ::ucarp
  contain ::ucarp::vip
}

class profile::haproxy {
  contain ::haproxy
}

 #now I can create a role using this profiles

class role::elb {
  contain ::profile::ucarp
  contain ::profile::haproxy
}

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

Из марионеточной документации

Having classes contain other classes can be very useful, especially in larger modules where you want to improve code readability by moving chunks of implementation into separate files.

    However, unlike resources, Puppet does not automatically contain classes when they are declared inside another class. This is because classes can be declared in several places via include and similar functions. Most of these places shouldn’t contain the class, and trying to contain it everywhere would cause huge problems.

    Instead, you must manually contain any classes that need to be contained.