Рекурсивно создавать дерево каталогов в марионетке без очистки

Я хочу создать структуру каталогов /var/www/apps/example/current/public, если она не существует, используя марионетку. Если он уже существует, я не хочу очищать содержимое каталогов. Как мне это сделать? Ниже я до сих пор:

file { "/var/www/apps/example/current/public":
  owner => 'deploy',
  group => 'users',
  ensure => "directory",
  purge => false,
  recurse => true
}

Это дает мне

 Cannot create /var/www/apps/example/current/public; parent directory /var/www/apps/example/current does not exist

Ответ 1

Параметр recurse не позволяет создавать родительские каталоги. Он используется для принудительного ввода значений свойств, таких как owner, mode и т.д. В содержимое каталога и подкаталоги рекурсивно.

file { '/var/www':
    owner   => 'www-data',
    recurse => true,
}

На самом деле, Puppet в настоящее время не может автоматически создавать все родительские каталоги. Вы должны добавить все соответствующие каталоги в качестве ресурсов.

file { [ '/var/www/apps',
         '/var/www/apps/example',
         '/var/www/apps/example/current',
         '/var/www/apps/example/current/public', ]:
           ensure => directory,
           ...
}

Существующий контент останется без изменений. Нет необходимости передавать параметр purge.

Ответ 2

  exec { "Create ${mydir}":
    creates => $mydir,
    command => "mkdir -p ${mydir}",
    path => $::path
  } -> file { $mydir : }

Последняя строка состоит в том, что другие ресурсы (например, файлы для создания внутри $mydir) могут зависеть от File[$mydir], как если бы можно было создать его с помощью простого старого блока file {}, который он действительно должен.

Ответ 3

Это правда, что марионетка не будет создавать родительские каталоги для вас, но вы можете легко создать файлового поставщика, который это сделает. Например, я создал настраиваемый тип и провайдер, чтобы в основном запускать "mkdir -p" в системах POSIX: https://docs.puppetlabs.com/puppet/latest/reference/lang_namespaces.html

Тем не менее, действительно хорошая причина в том, что Puppet не делает этого по умолчанию. Это потому, что Puppet не хочет отвечать за ваши разрешения в нескольких каталогах, поскольку агент работает от имени root. Это может быть плохо, если вы предоставляете /var/www или что-то в этом роде.

Параметр файла recuse действительно предназначен для управления параметрами дерева каталогов: https://docs.puppetlabs.com/references/latest/type.html#file-attribute-recurse

Вы можете создать дерево каталогов и выполнить его, используя, например, source = > 'puppet:///' uri, установить recurse в true и использовать все режимы файлов, которые устанавливаются в дереве каталогов, служил.

Ответ 4

Я попытался найти хорошее решение, но не смог. Поэтому я сам понял путь. Надеюсь, это полезно для других людей.

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

module Puppet::Parser::Functions
  newfunction(:parentdirs, :type => :rvalue, :doc => <<-EOS
    Build a list of all its parent directories.
    EOS
  ) do |arguments|

    raise(Puppet::ParseError, "parentdirs(): Wrong number of arguments " +
      "given (#{arguments.size} for 1)") if arguments.size < 1

    $dir_until = arguments.size > 1 ? arguments[1] : nil
    $cur = File.dirname(arguments[0])
    $result = []
    begin
        $result.unshift($cur)
        $last = $cur
        $cur = File.dirname($cur)
    end while $cur != $last and !$cur.end_with?('/') and $cur != $dir_until

    return $result
  end
end

Вот пример того, как его использовать:

$my_folder = '/var/www/apps/example/current/public'
$parent_dirs = parentdirs($my_folder, '/var/www/apps')
file { $parent_dirs:
  ensure => 'directory',
  owner => 'deploy',
  group => 'users'
}
file { $my_folder:
  ensure => 'directory',
  owner => 'deploy',
  group => 'anonymous'
}

Приведенные выше коды обеспечат создание папок '/var/www/apps/example' и '/var/www/apps/example/current', созданных до создания '/var/www/apps/example/current/public' while '/var/www/apps/example' и выше остаются не затронутыми.

Я тестировал его только в Windows. Но он должен работать в среде Linux.

Это не идеально. Но это лучше, чем перечисление всех родителей один за другим вручную.

Ответ 5

Если вы используете "define", вы можете иметь что-то вроде этого:

mymodule::recursive_dir { "My Directory" :
   drive => "C:",
   path  => "/path/to/folder",
}

Где я определяю "define" в mymodule.rb:

define mymodule::recursive_dir ($drive, $path) {
  $folders = split($path, "/")
  $folders.each |$index, $folder| {
    $calculated_folder = inline_template("<%= @folders[0, @index + 1].join('/') %>")
    $full_path = "${drive}${calculated_folder}"
    if (! defined(File[$full_path]) and $full_path != $drive) {
      file { $full_path :
        ensure => directory,
      }
    }
  }
}

Это разделяет путь и создает каждый каталог, поскольку он помещает путь обратно вместе, не пытаясь создать сам диск.

Ответ 6

Спасибо Энтони.

Я не знал, куда поместить код для его функции, и в результате моего исследования я переписал все это с использованием более нового синтаксиса, но сохранил его логику.

Мне потребовалось довольно много времени, чтобы решить все это, поэтому я решил, что было бы хорошо опубликовать это здесь.... Я бы просто добавил его в качестве комментария к его ответу, но, видимо, для этого мне нужно 50 баллов, а не ноль.

Код должен быть сохранен в своем собственном файле в каталоге среды Puppet следующим образом:

lib/puppet/functions/parentdirs.rb

... так что полный путь будет примерно таким (на сервере Ubuntu 18.04 с использованием пакетов Puppet, а не репо):

/etc/puppetlabs/code/environments/testing/lib/puppet/functions/parentdirs.rb

... кажется, есть и другие места, где вы можете это выразить, но это то, что я сделал.

Обратите внимание, что это файл .rb, а не .pp (потому что это код Ruby, а не Puppet).

Я получил большую часть своей информации с https://puppet.com/docs/puppet/5.5/functions_ruby_overview.html и с подстраниц.

Использование такое же, как и в оригинальной функции, и несколько пересмотрено в комментариях

# Returns an array of the parent directories to the given file or directory. This can then be passed to File to create the directory tree require for a dynamic path value.
# Parameter 2 is an optional, higher level of the same path. These higher level directories will not be in the array.
# Example 1: parameter 1 is '/var/www/mysite'; parameter 2 is not given; returns array ['/var', '/var/www']
# Example 2: parameter 1 is '/var/www/mysite'; parameter 2 is '/var'; returns array ['/var/www']

Puppet::Functions.create_function(:parentdirs) do

  dispatch :parents do
    required_param 'String', :target_dir
    optional_param 'String', :dir_until
    return_type 'Array'
  end


  def parents(target_dir, dir_until = '')

    cur = File.dirname(target_dir)
    result = []

    begin
        result.unshift(cur)
        last = cur
        cur = File.dirname(cur)
    end while cur != last and !cur.end_with?('/') and cur != dir_until

    return result

  end

end

Ответ 7

Вот чисто кукольное решение для mkdir -p $(dirname $file_path)

$file_path = '/tmp/foo/bar/bob.conf' # assumes file_path is Stdlib::Unixpath
# strip leading '/' then split and loop
$dirs = $file_path[1,-1].dirname.split('/').reduce([]) |$memo, $subdir| {
    $_dir =  $memo.empty ? { 
        true    => "/${subdir}",
        default => "${$memo[-1]}/${subdir}",
    }     
    concat($memo, $_dir)
}
file {$dirs:
    ensure => directory,
}