Fixing #206 and #422. Executables will still look for the deprecated config files and load them using the old parse method, but they now prefer a single configuration file, and files can set parameters (owner, mode, group) in brackets on the same line.

git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2464 980ebf18-57e1-0310-9a29-db15c13687c0
This commit is contained in:
luke 2007-05-06 05:42:53 +00:00
Родитель 1f8de9d0c2
Коммит 494675b1db
13 изменённых файлов: 416 добавлений и 139 удалений

Просмотреть файл

@ -1,3 +1,9 @@
Significantly reworking configuration parsing. Executables all now
look for 'puppet.conf' (#206), although they will parse the old-style
configuration files if they are present, although they throw a deprecation
warning. Also, file parameters (owner, mode, group) are now set on the
same line as the parameter, in brackets. (#422)
Added transaction summaries (available with the --summarize option),
useful for getting a quick idea of what happened in a transaction.
Currently only useful on the client or with the puppet interpreter.

Просмотреть файл

@ -161,9 +161,7 @@ elsif options[:verbose]
end
# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
Puppet.parse_config
Puppet.genconfig
Puppet.genmanifest

Просмотреть файл

@ -132,6 +132,8 @@ rescue GetoptLong::InvalidOption => detail
exit(1)
end
Puppet.parse_config
unless logset
Puppet::Util::Log.newdestination(:console)
end

Просмотреть файл

@ -152,9 +152,7 @@ rescue GetoptLong::InvalidOption => detail
end
# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
Puppet.parse_config
Puppet.genconfig
Puppet.genmanifest

Просмотреть файл

@ -272,9 +272,7 @@ rescue GetoptLong::InvalidOption => detail
end
# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
Puppet.parse_config
Puppet.genconfig
Puppet.genmanifest

Просмотреть файл

@ -293,6 +293,8 @@ config.header = "
Specifying Configuration Parameters
-----------------------------------
On The Command-Line
+++++++++++++++++++
Every Puppet executable (with the exception of ``puppetdoc``) accepts all of
the arguments below, but not all of the arguments make sense for every executable.
Each argument has a section listed with it in parentheses; often, that section
@ -321,22 +323,38 @@ on the command line::
The invocations above will enable and disable, respectively, the storage of
the client configuration.
Configuration Files
+++++++++++++++++++
As mentioned above, the configuration parameters can also be stored in a
configuration file located in the configuration directory (`/etc/puppet`
by default). The file is named for the executable it is intended for, for
example `/etc/puppetd.conf` is the configuration file for `puppetd`.
by default). All executables look for ``puppet.conf`` in their
configuration directory (although they used to each look to separate files).
The file, which follows INI-style formatting, should contain a bracketed
heading named for the executable, followed by pairs of parameters with their
values. Here is an example of a very simple ``puppetd.conf`` file::
All executables will set any parameters set within the ``main`` section,
while each executable will also look for a section named for the executable
and load those parameters. For example, ``puppetd`` will look for a
section named ``puppetd`, and ``puppetmasterd`` looks for a section
named ``puppetmasterd``. This allows you to use a single configuration file
to customize the settings for all of your executables.
[puppetd]
File Format
'''''''''''
The file follows INI-style formatting. Here is an example of a very simple
``puppet.conf`` file::
[main]
confdir = /private/puppet
storeconfigs = true
Note that boolean parameters must be explicitly specified as `true` or
`false` as seen above.
If you need to change file parameters (e.g., reset the mode or owner), do
so within curly braces on the same line::
[main]
myfile = /tmp/whatever {owner = root, mode = 644}
If you're starting out with a fresh configuration, you may wish to let
the executable generate a template configuration file for you by invoking
the executable in question with the `--genconfig` command. The executable

Просмотреть файл

@ -180,9 +180,7 @@ unless options[:setdest]
end
# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
Puppet.parse_config
Puppet.genconfig
Puppet.genmanifest

Просмотреть файл

@ -275,6 +275,8 @@ end
# Now parse the config
config = File.join(Puppet[:confdir], "puppetmasterd.conf")
Puppet.parse_config(config)
if File.exists? config
Puppet.config.parse(config)
end

Просмотреть файл

@ -133,9 +133,7 @@ if edit and host
end
# Now parse the config
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
Puppet.parse_config
typeobj = nil

Просмотреть файл

@ -184,6 +184,23 @@ module Puppet
timer
end
# Parse the config file for this process.
def self.parse_config(oldconfig = nil)
# First look for the old configuration file.
oldconfig ||= File.join(Puppet[:confdir], Puppet[:name].to_s + ".conf")
if FileTest.exists?(oldconfig)
Puppet.warning "Individual config files are deprecated; remove %s and use puppet.conf" % oldconfig
Puppet.config.old_parse(oldconfig)
return
end
# Now check for the normal config.
if Puppet[:config] and File.exists? Puppet[:config]
Puppet.debug "Parsing %s" % Puppet[:config]
Puppet.config.parse(Puppet[:config])
end
end
# Relaunch the executable.
def self.restart
command = $0 + " " + self.args.join(" ")

Просмотреть файл

@ -259,7 +259,7 @@ module Puppet
# Define the config default.
self.setdefaults(self.config[:name],
:config => ["$confdir/#{Puppet[:name]}.conf",
:config => ["$confdir/puppet.conf",
"The configuration file for #{Puppet[:name]}."],
:pidfile => ["", "The pid file"],
:bindaddress => ["", "The address to bind to. Mongrel servers

Просмотреть файл

@ -167,7 +167,7 @@ class Puppet::Util::Config
# Handle a command-line argument.
def handlearg(opt, value = nil)
value = mungearg(value) if value
value = munge_value(value) if value
str = opt.sub(/^--/,'')
bool = true
newstr = str.sub(/^no-/, '')
@ -221,18 +221,6 @@ class Puppet::Util::Config
end
end
# Convert arguments appropriately.
def mungearg(value)
# Handle different data types correctly
return case value
when /^false$/i: false
when /^true$/i: true
when /^\d+$/i: Integer(value)
else
value.gsub(/^["']|["']$/,'').sub(/\s+$/, '')
end
end
# Return all of the parameters associated with a given section.
def params(section)
section = section.intern if section.is_a? String
@ -243,8 +231,24 @@ class Puppet::Util::Config
}
end
# Parse a configuration file.
# Parse the configuration file.
def parse(file)
configmap = parse_file(file)
# We know we want the 'main' section
if main = configmap[:main]
set_parameter_hash(main)
end
# Otherwise, we only want our named section
if @config.include?(:name) and named = configmap[symbolize(self[:name])]
set_parameter_hash(named)
end
end
# Parse the configuration file. As of May 2007, this is a backward-compatibility method and
# will be deprecated soon.
def old_parse(file)
text = nil
if file.is_a? Puppet::Util::LoadedFile
@ -253,8 +257,8 @@ class Puppet::Util::Config
@file = Puppet::Util::LoadedFile.new(file)
end
# Create a timer so that this.
settimer()
# Don't create a timer for the old style parsing.
# settimer()
begin
text = File.read(@file.file)
@ -284,7 +288,7 @@ class Puppet::Util::Config
if var == :mode
value = $2
else
value = mungearg($2)
value = munge_value($2)
end
# Only warn if we don't know what this config var is. This
@ -706,6 +710,144 @@ Generated on #{Time.now}.
end
end
private
# Extra extra setting information for files.
def extract_fileinfo(string)
paramregex = %r{(\w+)\s*=\s*([\w\d]+)}
result = {}
string.scan(/\{\s*([^}]+)\s*\}/) do
params = $1
params.split(/\s*,\s*/).each do |str|
if str =~ /^\s*(\w+)\s*=\s*([\w\w]+)\s*$/
param, value = $1.intern, $2
result[param] = value
unless [:owner, :mode, :group].include?(param)
raise Puppet::Error, "Invalid file option '%s'" % param
end
if param == :mode and value !~ /^\d+$/
raise Puppet::Error, "File modes must be numbers"
end
else
raise Puppet::Error, "Could not parse '%s'" % string
end
end
return result
end
return nil
end
# Convert arguments into booleans, integers, or whatever.
def munge_value(value)
# Handle different data types correctly
return case value
when /^false$/i: false
when /^true$/i: true
when /^\d+$/i: Integer(value)
else
value.gsub(/^["']|["']$/,'').sub(/\s+$/, '')
end
end
# This is an abstract method that just turns a file in to a hash of hashes.
# We mostly need this for backward compatibility -- as of May 2007 we need to
# support parsing old files with any section, or new files with just two
# valid sections.
def parse_file(file)
text = nil
if file.is_a? Puppet::Util::LoadedFile
@file = file
else
@file = Puppet::Util::LoadedFile.new(file)
end
# Create a timer so that this file will get checked automatically
# and reparsed if necessary.
settimer()
begin
text = File.read(@file.file)
rescue Errno::ENOENT
raise Puppet::Error, "No such file %s" % file
rescue Errno::EACCES
raise Puppet::Error, "Permission denied to file %s" % file
end
result = Hash.new { |names, name|
names[name] = {}
}
count = 0
# Default to 'main' for the section.
section = :main
result[section][:_meta] = {}
text.split(/\n/).each { |line|
count += 1
case line
when /^\[(\w+)\]$/:
section = $1.intern # Section names
# Add a meta section
result[section][:_meta] ||= {}
when /^\s*#/: next # Skip comments
when /^\s*$/: next # Skip blanks
when /^\s*(\w+)\s*=\s*(.+)$/: # settings
var = $1.intern
# We don't want to munge modes, because they're specified in octal, so we'll
# just leave them as a String, since Puppet handles that case correctly.
if var == :mode
value = $2
else
value = munge_value($2)
end
# Check to see if this is a file argument and it has extra options
begin
if value.is_a?(String) and options = extract_fileinfo(value)
result[section][:_meta][var] = options
end
result[section][var] = value
rescue Puppet::Error => detail
detail.file = file
detail.line = line
raise
end
else
error = Puppet::Error.new("Could not match line %s" % line)
error.file = file
error.line = line
raise error
end
}
return result
end
# Take all members of a hash and assign their values appropriately.
def set_parameter_hash(params)
params.each do |param, value|
next if param == :_meta
unless @config.include?(param)
Puppet.warning "Discarded unknown configuration parameter %s" % param
next
end
self[param] = value
end
if meta = params[:_meta]
meta.each do |var, values|
values.each do |param, value|
@config[var].send(param.to_s + "=", value)
end
end
end
end
# The base element type.
class CElement
attr_accessor :name, :section, :default, :parent, :setbycli

Просмотреть файл

@ -2,6 +2,7 @@
$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'mocha'
require 'puppet'
require 'puppet/util/config'
require 'puppettest'
@ -196,7 +197,65 @@ class TestConfig < Test::Unit::TestCase
assert_equal("not default", @config[:yayness])
end
def test_parse
def test_parse_file
text = %{
one = this is a test
two = another test
owner = root
group = root
yay = /a/path
[main]
four = five
six = seven
[section1]
attr = value
owner = puppet
group = puppet
attrdir = /some/dir
attr3 = $attrdir/other
}
file = tempfile()
File.open(file, "w") { |f| f.puts text }
@config.expects(:settimer)
result = nil
assert_nothing_raised {
result = @config.send(:parse_file, file)
}
main = result[:main]
assert(main, "Did not get section for main")
{
:one => "this is a test",
:two => "another test",
:owner => "root",
:group => "root",
:yay => "/a/path",
:four => "five",
:six => "seven"
}.each do |param, value|
assert_equal(value, main[param], "Param %s was not set correctly in main" % param)
end
section1 = result[:section1]
assert(section1, "Did not get section1")
{
:attr => "value",
:owner => "puppet",
:group => "puppet",
:attrdir => "/some/dir",
:attr3 => "$attrdir/other"
}.each do |param, value|
assert_equal(value, section1[param], "Param %s was not set correctly in section1" % param)
end
end
def test_old_parse
text = %{
one = this is a test
two = another test
@ -233,7 +292,7 @@ yay = /a/path
}
assert_nothing_raised {
@config.parse(file)
@config.old_parse(file)
}
assert_equal("value", @config[:attr])
@ -267,6 +326,137 @@ yay = /a/path
check_to_transportable(@config)
end
def test_parse
result = {
:main => {:main => "main", :bad => "invalid"},
:puppet => {:other => "puppet"},
:puppetd => {:other => "puppetd"}
}
# Set our defaults, so they're valid. Don't define 'bad', since we want to test for failures.
@config.setdefaults(:main,
:main => ["whatever", "a"],
:other => ["a", "b"],
:name => ["puppet", "b"] # our default name
)
@config.expects(:parse_file).returns(result).times(2)
# First do it with our name being 'puppet'
assert_nothing_raised("Could not handle parse results") do
@config.parse(tempfile)
end
assert_logged(:warning, /unknown configuration parameter bad/, "Did not log invalid config param")
assert_equal("main", @config[:main], "Did not get main value")
assert_equal("puppet", @config[:other], "Did not get name value")
# Now switch names and make sure the parsing switches, too.
@config.clear
@config[:name] = :puppetd
assert_nothing_raised("Could not handle parse results") do
@config.parse(tempfile)
end
assert_logged(:warning, /unknown configuration parameter bad/, "Did not log invalid config param")
assert_equal("main", @config[:main], "Did not get main value")
assert_equal("puppetd", @config[:other], "Did not get name value")
end
# Make sure we can extract file options correctly.
def test_parsing_file_options
@config.setdefaults(:whev,
:file => {
:desc => "whev",
:default => "/default",
:owner => "me",
:group => "me",
:mode => "755"
}
)
file = tempfile
{
:pass => {
" {owner = you}" => {:owner => "you"},
" {owner = you, group = you}" => {:owner => "you", :group => "you"},
" {owner = you, group = you, mode = 755}" => {:owner => "you", :group => "you", :mode => "755"},
" { owner = you, group = you } " => {:owner => "you", :group => "you"},
"{owner=you,group=you} " => {:owner => "you", :group => "you"},
"{owner=you,} " => {:owner => "you"}
},
:fail => [
%{{owner = you group = you}},
%{{owner => you, group => you}},
%{{user => you}},
%{{random => you}},
%{{mode => you}}, # make sure modes are numbers
%{{owner => you}}
]
}.each do |type, list|
list.each do |value|
if type == :pass
value, should = value[0], value[1]
end
# Write our file out
File.open(file, "w") do |f|
f.puts %{[main]\nfile = /other%s} % value
end
if type == :fail
assert_raise(Puppet::Error, "Did not fail on %s" % value.inspect) do
@config.send(:parse_file, file)
end
else
result = nil
assert_nothing_raised("Failed to parse %s" % value.inspect) do
result = @config.send(:parse_file, file)
end
assert_equal(should, result[:main][:_meta][:file], "Got incorrect return for %s" % value.inspect)
end
end
end
end
# Make sure file options returned from parse_file are handled correctly.
def test_parsed_file_options
@config.setdefaults(:whev,
:file => {
:desc => "whev",
:default => "/default",
:owner => "me",
:group => "me",
:mode => "755"
}
)
result = {
:main => {
:file => "/other",
:_meta => {
:file => {
:owner => "you",
:group => "you",
:mode => "644"
}
}
}
}
@config.expects(:parse_file).returns(result)
assert_nothing_raised("Could not handle file options") do
@config.parse("/whatever")
end
# Get the actual object, so we can verify metadata
file = @config.element(:file)
assert_equal("you", file.owner, "Did not pass on user")
assert_equal("you", file.group, "Did not pass on group")
assert_equal("644", file.mode, "Did not pass on mode")
end
def test_arghandling
c = mkconfig
@ -517,7 +707,7 @@ yay = /a/path
group = "yayness"
File.open(cfile, "w") do |f|
f.puts "[#{Puppet[:name]}]
f.puts "[main]
group = #{group}
"
end
@ -624,7 +814,7 @@ yay = /a/path
File.open(file, "w") do |f|
f.puts %{
[mysection]
[main]
booltest = true
inttest = 27
}
@ -726,7 +916,7 @@ inttest = 27
file = tempfile()
# Set one parameter in the file
File.open(file, "w") { |f|
f.puts %{[mysection]\nfilechange = filevalue}
f.puts %{[main]\nfilechange = filevalue}
}
assert_nothing_raised {
config.parse(file)
@ -744,7 +934,7 @@ inttest = 27
# Now rewrite the file
File.open(file, "w") { |f|
f.puts %{[mysection]\nfilechange = newvalue}
f.puts %{[main]\nfilechange = newvalue}
}
cfile = config.file
@ -766,7 +956,7 @@ inttest = 27
file = tempfile()
# Set one parameter in the file
File.open(file, "w") { |f|
f.puts %{[mysection]\n
f.puts %{[main]\n
singleq = 'one'
doubleq = "one"
none = one
@ -793,7 +983,7 @@ inttest = 27
file = tempfile()
# Set one parameter in the file
File.open(file, "w") { |f|
f.puts %{[mysection]\n
f.puts %{[main]\n
paramdir = #{origpath}
}
}
@ -813,7 +1003,7 @@ inttest = 27
newpath = tempfile()
File.open(file, "w") { |f|
f.puts %{[mysection]\n
f.puts %{[main]\n
paramdir = #{newpath}
}
}
@ -941,7 +1131,7 @@ inttest = 27
config.setdefaults(:mysection, :one => ["yay", "yay"])
file = tempfile()
File.open(file, "w") { |f|
f.puts %{[mysection]\n
f.puts %{[main]\n
one = one
two = yay
}
@ -977,96 +1167,6 @@ inttest = 27
assert_equal("oneval/twoval/oneval/twoval", @config[:three],
"Did not interpolate curlied variables")
end
# #489
def test_modes
Puppet[:name] = "puppet"
config = tempfile()
check = Proc.new do |string, int|
trans = @config.section_to_transportable(:puppet)
ssldir = trans.find { |o| o.type == "file" }
assert(ssldir, "could not find trans object")
if ssldir[:mode].is_a?(Fixnum)
assert_equal(int, ssldir[:mode], "mode not set correctly")
else
assert_equal(string, ssldir[:mode], "mode not set correctly")
end
obj = nil
assert_nothing_raised { obj = ssldir.to_type }
assert(obj, "did not create object")
assert_equal(int, obj.should(:mode),
"did not pass mode correctly to file")
obj.class.clear
end
file = tempfile
@config.setdefaults(:puppet, :mode => ["644", "yay"])
@config.setdefaults(:puppet, :ssldir => {
:mode => 0644,
:desc => "yay",
:default => "/some/file"})
# Convert it first using the number
check.call("644", 0644)
File.open(config, "w") { |f| f.puts "[puppet]
mode = 750
ssldir = #{file}
"}
@config.parse(config)
assert_equal("750", @config[:mode],
"Did not parse mode correctly")
check.call("750", 0750)
end
def test_only_set_metas_when_valid
file = tempfile
config = tempfile
@config.setdefaults(Puppet[:name], :ssldir => {
:mode => 0644,
:group => "yayness",
:desc => "yay",
:default => "/some/file"})
File.open(config, "w") { |f| f.puts "[#{Puppet[:name]}]
mode = 755
group = foo
ssldir = #{file}
"}
assert_nothing_raised do
@config.parse(config)
end
assert_raise(ArgumentError) do
@config[:mode]
end
assert_raise(ArgumentError) do
@config[:group]
end
# Now make them valid params
@config.setdefaults(Puppet[:name], :group => ["blah", "yay"])
@config.setdefaults(Puppet[:name], :mode => ["750", "yay"])
assert_nothing_raised do
@config.parse(config)
end
assert_equal("foo", @config[:group],
"Did not store group when it is a valid config")
assert_equal("755", @config[:mode],
"Did not store mode when it is a valid config")
end
end
# $Id$