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

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

Когда я работаю с марионеткой, мне хочется автоматизировать более сложные настройки, например vhosts для X сайтов. По мере того, как моя марионетка становится все более сложной, мне становится все труднее применять принцип СУХОЙ (не повторяйтесь). Ниже приведен упрощенный фрагмент того, что мне нужно, но он не работает, потому что марионетка выдает различные ошибки в зависимости от того, использую ли я классы или определяю. Я хотел бы получить отзывы от опытных кукловодов о том, как они могут подойти к этому решению.

# site.pp
import 'nodes'

# nodes.pp
node nodes_dev {
    $service_env = 'dev'
}

node nodes_prod {
    $service_env = 'prod'
}

import 'nodes/dev'
import 'nodes/prod'

# nodes/dev.pp
node 'service1.ownij.lan' inherits nodes_dev {
    httpd::vhost::package::site { 'foo': }
    httpd::vhost::package::site { 'bar': }
}

# modules/vhost/package.pp
class httpd::vhost::package {

    class manage($port) {

        # More complex stuff goes here like ensuring that conf paths and uris exist
        # As well as log files, which is I why I want to do the work once and use many

        notify { $service_env: }
        notify { $port: }
    }    

    define site {

        case $name {

            'foo': {

                class 'httpd::vhost::package::manage':
                    port => 20000
                }
            }

            'bar': {

                class 'httpd::vhost::package::manage':
                    port => 20001
                }
            }
        }
    }
}

Этот фрагмент кода дает мне Duplicate declaration: Class[Httpd::Vhost::Package::Manage] ошибка, и если я переключу manage class в определение и попытаться получить доступ к глобальной переменной или передать переменную, общую для foo и bar, я получаю Duplicate declaration: Notify[dev] ошибка.

Есть предложения, как я могу реализовать принцип DRY и при этом заставить марионетку работать?

-- ОБНОВИТЬ --

У меня все еще есть проблема, пытаясь убедиться, что некоторые из моих хостов, которые могут совместно использовать родительский каталог, настроены правильно. Что-то вроде этого:

node 'service1.ownij.lan' inherits nodes_dev {
    httpd::vhost::package::site { 'foo_sitea': }
    httpd::vhost::package::site { 'foo_siteb': }
    httpd::vhost::package::site { 'bar': }
}

Что мне нужно, так это то, что sitea и siteb имеют одну и ту же родительскую папку "foo". Проблема, с которой я сталкиваюсь, заключается в том, что я вызываю определение, чтобы убедиться, что папка «foo» существует. Ниже приводится определение сайта в том виде, в котором он есть у меня, надеюсь, он будет иметь смысл, что я пытаюсь достичь.

class httpd::vhost::package {

    File {
        owner   => root,
        group   => root,
        mode    => 0660
    }

    define site() {

        $app_parts = split($name, '[_]')

        $app_primary = $app_parts[0]

        if ($app_parts[1] == '') {
            $tpl_path_partial_app = "${app_primary}"
            $app_sub = ''
        } else {
            $tpl_path_partial_app = "${app_primary}/${app_parts[1]}"
            $app_sub = $app_parts[1]
        }

        include httpd::vhost::log::base

        httpd::vhost::log::app { $name:
            app_primary => $app_primary,
            app_sub     => $app_sub
        }
    }
}

class httpd::vhost::log {

    class base {

        $paths = [ '/tmp', '/tmp/var', '/tmp/var/log', '/tmp/var/log/httpd', "/tmp/var/log/httpd/${service_env}" ]

        file { $paths:
            ensure  => directory
        }
    }

    define app($app_primary, $app_sub) {

        $paths = [ "/tmp/var/log/httpd/${service_env}/${app_primary}", "/tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}" ]

        file { $paths:
            ensure  => directory
        }
    }
}

В include httpd::vhost::log::base работает нормально, потому что он "включен", что означает, что он реализуется только один раз, даже если site вызывается несколько раз. Я получаю следующую ошибку: Duplicate declaration: File[/tmp/var/log/httpd/dev/foo]. Я изучал использование exec, но я не уверен, что это правильный путь, наверняка другим приходилось иметь дело с этим раньше, и любое понимание приветствуется, поскольку я боролся с этим в течение нескольких недель. Спасибо.

Во-первых, вам обязательно стоит использовать define для httpd::vhost::package::manageне class, поскольку он будет определен несколько раз для одного и того же узла.

notify { $service_env: } всегда будет взрывать вещи, так как $service_env является dev для обоих звонков define. Вы никогда не сможете повторно объявить один и тот же ресурс с тем же именем.

Вы можете исправить это, переключившись на что-то вроде notify { "${service_env}-${port}": }, но вы, вероятно, столкнетесь с другими проблемами, если он не был создан должным образом для обработки настройки как определенного типа.

Структура классов полностью расширяется до httpd::vhost::package::manage это странно - я бы порекомендовал сократить все до структуры классов, которая упростит работу и позволит вам иметь только необходимые ресурсы.

httpd (init.pp - have it include the install, config, service classes)
httpd::install (install.pp - have it define Package[httpd] to install)
httpd::service (service.pp - have it define Service[httpd]; the vhost defines can
                notify this to update)
httpd::config (config.pp - any configuration, such as handling of httpd.conf,
               that won't be per-vhost)
httpd::vhost (vhost.pp - the defined type that can be used multiple times.  Have
              it include httpd so that the base classes are handled, and
              notify httpd::service from your vhost config file resources)

На самом деле простой пример управления виртуальным хостом пример для определенных типов в документации - дайте мне знать, если вам понадобятся какие-либо другие разъяснения о том, как сделать из этого красивую многоразовую конфигурацию!

Редактировать:

Обновление для проблемы общих родительских каталогов.

Управление файлом из двух разных мест - это боль в Puppet, и раньше у меня тоже возникали проблемы. Особенно неприятно, когда нет конфликта между двумя объявлениями ресурсов, как в вашей текущей головоломке.

Вы правы, что exec ресурс - вероятно, ваш лучший ответ.

define app($app_primary, $app_sub) {
    exec { "mkdir_${service_env}_${app_primary}_${app_sub}":
        command  => "/bin/mkdir -p /tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}",
        creates  => "/tmp/var/log/httpd/${service_env}/${app_primary}/${app_sub}",
        require  => Class["httpd::vhost::log::base"],
    }
}

Это работает в обоих случаях (с подкаталогом и без него), потому что вы устанавливаете $app_sub в пустую строку, если она не используется.

Я бы сказал, что, вероятно, будет намного проще просто не делиться деревом каталогов (используйте log/httpd/foo_sitea как каталог журнала вместо log/httpd/foo/sitea), но это должно помочь. Разделение строки и разное поведение в зависимости от этого, и все это отдельные defineкогда это действительно может жить в site класс .. не усложняйте!