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

У меня проблемы с дизайном кода Puppet (версия 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, удалите параметр after и замените его на определенный тип, например systemd::service::after который управляет соответствующей записью в файле определения сервиса. Это то, чего я действительно не хочу делать. Обычно я уклоняюсь от модификации модулей кузницы, так как это заставляет меня поддерживать их самостоятельно. В этом случае изменение также кажется довольно большим (включая изменение интерфейса).
  2. Введите мой собственный определенный тип в модуль ucarp ucarp::order_after который ничего не делает. Модули профиля будут определять виртуальные экземпляры этого типа. Затем класс ucarp может использовать запрос puppetdb для сбора всех экземпляров ucarp::order_after, Большой недостаток в том, что я имею дело только с виртуальными ресурсами, а не с экспортированными. Так что на самом деле нет необходимости привлекать puppetdb, что превращает этот подход в уродливый обходной путь.

Дальнейшее решение вдохновлено c4f4t0r:

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

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

1 ответ

Вы должны использовать функцию содержать и поместить параметры класса в 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.
Другие вопросы по тегам