class KafoParsers::PuppetStringsModuleParser

Public Class Methods

available?() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 29
def self.available?
  _stdout, _stderr, status = run_puppet(['help', 'strings'])
  if status.success?
    return true
  else
    raise KafoParsers::ParserNotAvailable.new("#{puppet_bin} does not have strings module installed.")
  end
end
new(file) click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 38
def initialize(file)
  @file = file = File.expand_path(file)
  raise KafoParsers::ModuleName, "File not found #{file}, check your answer file" unless File.exist?(file)

  command = ['strings', 'generate', '--format', 'json', file]
  @raw_json, stderr, status = self.class.run_puppet(command)

  unless status.success?
    raise KafoParsers::ParseError, "'#{command}' returned error:\n  #{@raw_json}\n  #{stderr}"
  end

  begin
    @complete_hash = ::JSON.parse(@raw_json)
  rescue ::JSON::ParserError => e
    raise KafoParsers::ParseError, "'#{command}' returned invalid json output: #{e.message}\n#{@raw_json}"
  end
  self.data_type # we need to determine data_type before any further parsing

  self
end
parse(file) click to toggle source

You can call this method to get all supported information from a given manifest

@param [ String ] manifest file path to parse @return [ Hash ] hash containing values, validations, documentation, types, groups and conditions

# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 14
def self.parse(file)
  content = new(file)
  docs    = content.docs

  # data_type must be called before other validations
  data = {
    :object_type => content.data_type,
    :values      => content.values,
    :validations => content.validations
  }
  data[:parameters] = data[:values].keys
  data.merge!(docs)
  data
end
puppet_bin() click to toggle source

AIO and system default puppet bins are tested for existence, fallback to just `puppet` otherwise

# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 60
def self.puppet_bin
  @puppet_bin ||= begin
    found_puppet_path = (::ENV['PATH'].split(File::PATH_SEPARATOR) + ['/opt/puppetlabs/bin']).find do |path|
      binary = File.join(path, 'puppet')
      binary if File.executable?(binary)
    end
    found_puppet_path.nil? ? 'puppet' : File.join(found_puppet_path, 'puppet')
  end
end

Private Class Methods

clean_env_vars() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 162
def self.clean_env_vars
  # Cleaning ENV vars and keeping required vars only because,
  # When using SCL it adds GEM_HOME and GEM_PATH ENV vars.
  whitelisted_vars = %w[HOME USER LANG]

  cleaned_env = ::ENV.select { |var| whitelisted_vars.include?(var) || var.start_with?('LC_') }
  cleaned_env['PATH'] = '/sbin:/bin:/usr/sbin:/usr/bin:/opt/puppetlabs/bin'
  cleaned_env
end
is_aio_puppet?() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 145
def self.is_aio_puppet?
  puppet_command = search_puppet_path('puppet')
  File.realpath(puppet_command).start_with?('/opt/puppetlabs')
rescue Errno::ENOENT
  false
end
run_puppet(command) click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 152
def self.run_puppet(command)
  command = command.unshift(self.puppet_bin)

  if is_aio_puppet?
    Open3.capture3(clean_env_vars, *command, :unsetenv_others => true)
  else
    Open3.capture3(::ENV, *command, :unsetenv_others => false)
  end
end
search_puppet_path(bin_name) click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 136
def self.search_puppet_path(bin_name)
  # Find the location of the puppet executable and use that to
  # determine the path of all executables
  bin_path = (::ENV['PATH'].split(File::PATH_SEPARATOR) + ['/opt/puppetlabs/bin']).find do |path|
    File.executable?(File.join(path, 'puppet')) && File.executable?(File.join(path, bin_name))
  end
  File.join([bin_path, bin_name].compact)
end

Public Instance Methods

data_type() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 70
def data_type
  @data_type ||= begin
    if (@parsed_hash = @complete_hash['puppet_classes'].find { |klass| klass['file'] == @file })
      'hostclass'
    elsif (@parsed_hash = @complete_hash['defined_types'].find { |klass| klass['file'] == @file })
      'definition'
    else
      raise KafoParsers::ParseError, "unable to find manifest data, syntax error in manifest #{@file}?"
    end
  end
end
docs() click to toggle source

returns data in following form {

:docs => { $param1 => ['documentation without types and conditions']}
:types => { $param1 => 'boolean'},
:groups => { $param1 => ['Parameters', 'Advanced']},
:conditions => { $param1 => '$db_type == "mysql"'},

}

# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 102
def docs
  data = { :docs => {}, :types => {}, :groups => {}, :conditions => {} }
  if @parsed_hash.nil?
    raise KafoParsers::DocParseError, "no documentation found for manifest #{@file}, parsing error?"
  elsif !@parsed_hash['docstring'].nil? && !@parsed_hash['docstring']['text'].nil?
    # Lowest precedence: types given in the strings hash from class definition
    tag_params.each do |param|
      data[:types][param['name']] = param['types'][0] unless param['types'].nil?
    end

    # Next: types and other data from RDoc parser
    rdoc_parser = DocParser.new(@parsed_hash['docstring']['text']).parse
    data[:docs] = rdoc_parser.docs
    data[:groups] = rdoc_parser.groups
    data[:conditions] = rdoc_parser.conditions
    data[:types].merge! rdoc_parser.types

    # Highest precedence: data in YARD @param stored in the 'text' field
    tag_params.each do |param|
      param_name = param['name']
      unless param['text'].nil? || param['text'].empty?
        param_parser = ParamDocParser.new(param_name, param['text'].split($/))
        data[:docs][param_name] = param_parser.doc if param_parser.doc
        data[:groups][param_name] = param_parser.group if param_parser.group
        data[:conditions][param_name] = param_parser.condition if param_parser.condition
        data[:types][param_name] = param_parser.type if param_parser.type
      end
    end
  end
  data
end
validations(param = nil) click to toggle source

unsupported in puppet strings parser

# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 91
def validations(param = nil)
  []
end
values() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 82
def values
  Hash[
    # combine known parameters (from tags) with any defaults provided
    tag_params.select { |param| !param['types'].nil? }.map { |param| [ param['name'], nil ] } +
      @parsed_hash.fetch('defaults', {}).map { |name, value| [ name, value.nil? ? nil : sanitize(value) ] }
  ]
end

Private Instance Methods

sanitize(value) click to toggle source

default values using puppet strings includes $ symbol, e.g. “$::foreman::params::ssl”

values are reported as strings which is issue especially for :under strings are double quoted basic array and hashes are supported others must be typecast manually according to reported type

# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 178
def sanitize(value)
  if (value.start_with?("'") && value.end_with?("'")) || (value.start_with?('"') && value.end_with?('"'))
    value = value[1..-2]
  elsif value.start_with?('[') && value.end_with?(']')
    # TODO: handle commas in strings like ["a,b", "c"]
    value = value[1..-2].split(',').map { |v| sanitize(v.strip) }
  elsif value.start_with?('{') && value.end_with?('}')
    # TODO: handle commas and => in strings, like {"key" => "value,=>"}
    value = value[1..-2].split(',').map do |v|
      split = v.strip.split('=>')
      raise 'Invalid hash' unless split.length == 2
      split.map { |s| sanitize(s.strip) }
    end.to_h
  end
  value = :undef if value == 'undef'

  value
end
tag_params() click to toggle source
# File lib/kafo_parsers/puppet_strings_module_parser.rb, line 197
def tag_params
  if @parsed_hash['docstring']['tags']
    @parsed_hash['docstring']['tags'].select { |tag| tag['tag_name'] == 'param' }
  else
    []
  end
end