1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
require_relative '../../puppet/settings/ini_file'
##
# @api private
#
# Parses puppet configuration files
#
class Puppet::Settings::ConfigFile
##
# @param value_converter [Proc] a function that will convert strings into ruby types
def initialize(value_converter)
@value_converter = value_converter
end
# @param file [String, File] pointer to the file whose text we are parsing
# @param text [String] the actual text of the inifile to be parsed
# @param allowed_section_names [Array] an optional array of accepted section
# names; if this list is non-empty, sections outside of it will raise an
# error.
# @return A Struct with a +sections+ array representing each configuration section
def parse_file(file, text, allowed_section_names = [])
result = Conf.new
if !allowed_section_names.empty?
allowed_section_names << 'main' unless allowed_section_names.include?('main')
end
ini = Puppet::Settings::IniFile.parse(text.encode(Encoding::UTF_8))
unique_sections_in(ini, file, allowed_section_names).each do |section_name|
section = Section.new(section_name.to_sym)
result.with_section(section)
ini.lines_in(section_name).each do |line|
if line.is_a?(Puppet::Settings::IniFile::SettingLine)
parse_setting(line, section)
elsif line.text !~ /^\s*#|^\s*$/
raise Puppet::Settings::ParseError.new(_("Could not match line %{text}") % { text: line.text }, file, line.line_number)
end
end
end
result
end
Conf = Struct.new(:sections) do
def initialize
super({})
end
def with_section(section)
sections[section.name] = section
self
end
end
Section = Struct.new(:name, :settings) do
def initialize(name)
super(name, [])
end
def with_setting(name, value, meta)
settings << Setting.new(name, value, meta)
self
end
def setting(name)
settings.find { |setting| setting.name == name }
end
end
Setting = Struct.new(:name, :value, :meta) do
def has_metadata?
meta != NO_META
end
end
Meta = Struct.new(:owner, :group, :mode)
NO_META = Meta.new(nil, nil, nil)
private
def unique_sections_in(ini, file, allowed_section_names)
ini.section_lines.collect do |section|
if !allowed_section_names.empty? && !allowed_section_names.include?(section.name)
error_location_str = Puppet::Util::Errors.error_location(file, section.line_number)
message = _("Illegal section '%{name}' in config file at %{error_location}.") %
{ name: section.name, error_location: error_location_str }
#TRANSLATORS 'puppet.conf' is the name of the puppet configuration file and should not be translated.
message += ' ' + _("The only valid puppet.conf sections are: [%{allowed_sections_list}].") %
{ allowed_sections_list: allowed_section_names.join(", ") }
message += ' ' + _("Please use the directory environments feature to specify environments.")
message += ' ' + _("(See https://puppet.com/docs/puppet/latest/environments_about.html)")
raise(Puppet::Error, message)
end
section.name
end.uniq
end
def parse_setting(setting, section)
var = setting.name.intern
value = @value_converter[setting.value]
# Check to see if this is a file argument and it has extra options
begin
options = extract_fileinfo(value) if value.is_a?(String)
if options
section.with_setting(var, options[:value], Meta.new(options[:owner],
options[:group],
options[:mode]))
else
section.with_setting(var, value, NO_META)
end
rescue Puppet::Error => detail
raise Puppet::Settings::ParseError.new(detail.message, file, setting.line_number, detail)
end
end
def empty_section
{ :_meta => {} }
end
def extract_fileinfo(string)
result = {}
value = string.sub(/\{\s*([^}]+)\s*\}/) do
params = $1
params.split(/\s*,\s*/).each do |str|
if str =~ /^\s*(\w+)\s*=\s*([\w]+)\s*$/
param, value = $1.intern, $2
result[param] = value
unless [:owner, :mode, :group].include?(param)
raise ArgumentError, _("Invalid file option '%{parameter}'") % { parameter: param }
end
if param == :mode and value !~ /^\d+$/
raise ArgumentError, _("File modes must be numbers")
end
else
raise ArgumentError, _("Could not parse '%{string}'") % { string: string }
end
end
''
end
result[:value] = value.sub(/\s*$/, '')
result
end
end
|