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

У меня проблемы с дизайном кода марионетки (версия 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, который собирает все параметры после в одну строку, которая может быть перешел в systemd :: service. Как мне это сделать?

В настоящее время мне приходят на ум два возможных решения:

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

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

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

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

2
задан 11 March 2019 в 17:11
1 ответ

Вам нужно использовать функцию 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.
2
ответ дан 3 December 2019 в 11:23

Теги

Похожие вопросы