Class: PDK::Module::TemplateDir

Inherits:
Object
  • Object
show all
Defined in:
lib/pdk/module/templatedir.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path_or_url, module_metadata = {}, init = false) {|self| ... } ⇒ TemplateDir

Initialises the TemplateDir object with the path or URL to the template and the block of code to run to be run while the template is available.

The template directory is only guaranteed to be available on disk within the scope of the block passed to this method.

template or a URL to a git repository. Defaults to an empty Hash. the template available on disk.

Examples:

Using a git repository as a template

PDK::Module::TemplateDir.new('https://github.com/puppetlabs/pdk-templates') do |t|
  t.render do |filename, content|
    File.open(filename, 'w') do |file|
      file.write(content)
    end
  end
end

Parameters:

  • path_or_url (String)

    The path to a directory to use as the

  • module_metadata (Hash) (defaults to: {})

    A Hash containing the module metadata.

Yield Parameters:

Raises:

  • (ArgumentError)

    If no block is given to this method.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/pdk/module/templatedir.rb', line 40

def initialize(path_or_url,  = {}, init = false)
  unless block_given?
    raise ArgumentError, _('%{class_name} must be initialized with a block.') % { class_name: self.class.name }
  end

  if PDK::Util::Git.repo?(path_or_url)
    @path = self.class.clone_template_repo(path_or_url)
    @repo = path_or_url
  else
    @path = path_or_url
  end

  @init = init
  @moduleroot_dir = File.join(@path, 'moduleroot')
  @moduleroot_init = File.join(@path, 'moduleroot_init')
  @dirs = [@moduleroot_dir]
  @dirs << @moduleroot_init if @init
  @object_dir = File.join(@path, 'object_templates')

  validate_module_template!

  @module_metadata = 

  yield self
ensure
  # If we cloned a git repo to get the template, remove the clone once
  # we're done with it.
  if @repo
    FileUtils.remove_dir(@path)
  end
end

Instance Attribute Details

#module_metadataObject

Returns the value of attribute module_metadata.



11
12
13
# File 'lib/pdk/module/templatedir.rb', line 11

def 
  @module_metadata
end

Class Method Details

.clone_template_repo(origin_repo) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Path to working directory into which template repo has been cloned and reset.

Returns:

  • (String)

    Path to working directory into which template repo has been cloned and reset

Raises:



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/pdk/module/templatedir.rb', line 293

def self.clone_template_repo(origin_repo)
  # @todo When switching this over to using rugged, cache the cloned
  # template repo in `%AppData%` or `$XDG_CACHE_DIR` and update before
  # use.
  temp_dir = PDK::Util.make_tmpdir_name('pdk-templates')
  git_ref = (origin_repo == PDK::Util.default_template_url) ? PDK::Util.default_template_ref : 'origin/master'

  clone_result = PDK::Util::Git.git('clone', origin_repo, temp_dir)

  if clone_result[:exit_code].zero?
    Dir.chdir(temp_dir) do
      reset_result = PDK::Util::Git.git('reset', '--hard', git_ref)
      unless reset_result[:exit_code].zero?
        PDK.logger.error reset_result[:stdout]
        PDK.logger.error reset_result[:stderr]
        raise PDK::CLI::FatalError, _("Unable to set HEAD of git repository at '%{repo}' to ref:'%{ref}'.") % { repo: temp_dir, ref: git_ref }
      end
    end
  else
    PDK.logger.error clone_result[:stdout]
    PDK.logger.error clone_result[:stderr]
    raise PDK::CLI::FatalError, _("Unable to clone git repository at '%{repo}' into '%{dest}'.") % { repo: origin_repo, dest: temp_dir }
  end

  PDK::Util.canonical_path(temp_dir)
end

.files_in_template(dirs) ⇒ Hash{String=>String}

Get a list of template files in the template directory.

value locations.

Returns:

  • (Hash{String=>String})

    A hash of key file names and



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/pdk/module/templatedir.rb', line 214

def self.files_in_template(dirs)
  temp_paths = []
  dirlocs = []
  dirs.each do |dir|
    raise ArgumentError, _("The directory '%{dir}' doesn't exist") % { dir: dir } unless Dir.exist?(dir)
    temp_paths += Dir.glob(File.join(dir, '**', '*'), File::FNM_DOTMATCH).select do |template_path|
      if File.file?(template_path) && !File.symlink?(template_path)
        dirlocs << dir
      end
    end
    temp_paths.map do |template_path|
      template_path.sub!(%r{\A#{Regexp.escape(dir)}#{Regexp.escape(File::SEPARATOR)}}, '')
    end
  end
  Hash[temp_paths.zip dirlocs]
end

Instance Method Details

#config_for(dest_path, sync_config_path = nil) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate a hash of data to be used when rendering the specified template.

data is for, relative to the root of the module.

‘@configs` instance variable.

Parameters:

  • dest_path (String)

    The destination path of the file that the

Returns:

  • (Hash)

    The data that will be available to the template via the



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/pdk/module/templatedir.rb', line 241

def config_for(dest_path, sync_config_path = nil)
  module_root = PDK::Util.module_root
  sync_config_path ||= File.join(module_root, '.sync.yml') unless module_root.nil?
  config_path = File.join(@path, 'config_defaults.yml')

  if @config.nil?
    conf_defaults = read_config(config_path)
    sync_config = read_config(sync_config_path) unless sync_config_path.nil?
    @config = conf_defaults
    @config.deep_merge!(sync_config, knockout_prefix: '---') unless sync_config.nil?
  end
  file_config = @config.fetch(:global, {})
  file_config['module_metadata'] = @module_metadata
  file_config.merge!(@config.fetch(dest_path, {})) unless dest_path.nil?
  file_config.merge!(@config)
end

#metadataHash{String => String}

Retrieve identifying metadata for the template.

For git repositories, this will return the URL to the repository and a reference to the HEAD.

Returns:

  • (Hash{String => String})

    A hash of identifying metadata.



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/pdk/module/templatedir.rb', line 80

def 
  result = {
    'pdk-version' => PDK::Util::Version.version_string,
  }

  result['template-url'] = @repo ? @repo : @path

  ref_result = PDK::Util::Git.git('--git-dir', File.join(@path, '.git'), 'describe', '--all', '--long', '--always')
  result['template-ref'] = ref_result[:stdout].strip if ref_result[:exit_code].zero?

  result
end

#object_configHash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate a hash of data to be used when rendering object templates.

Read ‘config_defaults.yml` from the root of the template directory (if it exists) build a hash of values from the value of the `:global` key.

‘@configs` instance variable.

Returns:

  • (Hash)

    The data that will be available to the template via the



174
175
176
# File 'lib/pdk/module/templatedir.rb', line 174

def object_config
  config_for(nil)
end

#object_template_for(object_type) ⇒ Hash{Symbol => String}

Searches the template directory for template files that can be used to render files for the specified object type.

‘:defined_type`, `:fact`, etc).

template dir, otherwise ‘nil`. The returned hash can contain two keys, :object contains the path on disk to the template for the object, :spec contains the path on disk to the template for the object’s spec file (if available).

Parameters:

  • object_type (Symbol)

    The object type, e.g. (‘:class`,

Returns:

  • (Hash{Symbol => String})

    if the templates are available in the



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/pdk/module/templatedir.rb', line 147

def object_template_for(object_type)
  object_path = File.join(@object_dir, "#{object_type}.erb")
  type_path = File.join(@object_dir, "#{object_type}_type.erb")
  spec_path = File.join(@object_dir, "#{object_type}_spec.erb")
  type_spec_path = File.join(@object_dir, "#{object_type}_type_spec.erb")

  if File.file?(object_path) && File.readable?(object_path)
    result = { object: object_path }
    result[:type] = type_path if File.file?(type_path) && File.readable?(type_path)
    result[:spec] = spec_path if File.file?(spec_path) && File.readable?(spec_path)
    result[:type_spec] = type_spec_path if File.file?(type_spec_path) && File.readable?(type_spec_path)
    result
  else
    nil
  end
end

#read_config(loc) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generates a hash of data from a given yaml file location.

if so.

Parameters:

  • loc (String)

    The path of the yaml config file.

Returns:

  • (Hash)

    The data that has been read in from the given yaml file.



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/pdk/module/templatedir.rb', line 268

def read_config(loc)
  if File.file?(loc) && File.readable?(loc)
    begin
      YAML.safe_load(File.read(loc), [], [], true)
    rescue Psych::SyntaxError => e
      PDK.logger.warn _("'%{file}' is not a valid YAML file: %{problem} %{context} at line %{line} column %{column}") % {
        file:    loc,
        problem: e.problem,
        context: e.context,
        line:    e.line,
        column:  e.column,
      }
      {}
    end
  else
    {}
  end
end

#render {|dest_path, dest_content| ... } ⇒ void

This method returns an undefined value.

Loop through the files in the template, yielding each rendered file to the supplied block.

relative to the root of the module. destination file.

Yield Parameters:

  • dest_path (String)

    The path of the destination file,

  • dest_content (String)

    The rendered content of the

Raises:



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pdk/module/templatedir.rb', line 106

def render
  PDK::Module::TemplateDir.files_in_template(@dirs).each do |template_file, template_loc|
    template_file = template_file.to_s
    PDK.logger.debug(_("Rendering '%{template}'...") % { template: template_file })
    dest_path = template_file.sub(%r{\.erb\Z}, '')
    config = config_for(dest_path)
    dest_status = :manage

    if config['unmanaged']
      dest_status = :unmanage
    elsif config['delete']
      dest_status = :delete
    else
      begin
        dest_content = PDK::TemplateFile.new(File.join(template_loc, template_file), configs: config, template_dir: self).render
      rescue => e
        error_msg = _(
          "Failed to render template '%{template}'\n" \
          '%{exception}: %{message}',
        ) % { template: template_file, exception: e.class, message: e.message }
        raise PDK::CLI::FatalError, error_msg
      end
    end

    yield dest_path, dest_content, dest_status
  end
end

#validate_module_template!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Validate the content of the template directory.

a directory called ‘moduleroot’.

Raises:

  • (ArgumentError)

    If the specified path is not a directory.

  • (ArgumentError)

    If the template directory does not contain



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/pdk/module/templatedir.rb', line 187

def validate_module_template!
  # rubocop:disable Style/GuardClause
  unless File.directory?(@path)
    if PDK::Util.package_install? && File.fnmatch?(File.join(PDK::Util.package_cachedir, '*'), @path)
      raise ArgumentError, _('The built-in template has substantially changed. Please run "pdk convert" on your module to continue.')
    else
      raise ArgumentError, _("The specified template '%{path}' is not a directory.") % { path: @path }
    end
  end

  unless File.directory?(@moduleroot_dir)
    raise ArgumentError, _("The template at '%{path}' does not contain a 'moduleroot/' directory.") % { path: @path }
  end

  unless File.directory?(@moduleroot_init)
    # rubocop:disable Metrics/LineLength
    raise ArgumentError, _("The template at '%{path}' does not contain a 'moduleroot_init/' directory, which indicates you are using an older style of template. Before continuing please use the --template-url flag when running the pdk new commands to pass a new style template.") % { path: @path }
    # rubocop:enable Metrics/LineLength Style/GuardClause
  end
end