Merge commit 'turnbull/0.24.x'

This commit is contained in:
Luke Kanies 2008-08-26 22:40:26 -07:00
Родитель 7034882fdf b50e718490
Коммит 5b9dd01326
20 изменённых файлов: 282 добавлений и 239 удалений

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

@ -8,6 +8,19 @@
set file paths to 'false' to disable the CRL.
0.24.x
Added simple rake task for running unit tests
Added spec Rake task
Fixed #1526 - Fixed leak in template
Fixed #1506 - Removed storeconfig duplicate indexes
Fixed #1457 - case insensitive match for error
Fixed #1488 - Moved individual functions out of functions.rb into
lib/puppet/parser/functions directory. New functions should be create in this directory.
Fixed #1508 - Added HP-UX package provider
Fixed #1502 - Fixed poor stored configuration performance

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

@ -144,3 +144,20 @@ task :tracdocs do
sh "puppetdoc -m trac -r #{ref.to_s}"
end
end
desc "Run the specs under spec/"
task :spec do
require 'spec'
require 'spec/rake/spectask'
require 'rcov'
Spec::Rake::SpecTask.new do |t|
# t.rcov = true
t.spec_opts = ['--format','s', '--loadby','mtime']
t.spec_files = FileList['spec/**/*.rb']
end
end
desc "Run the unit tests"
task :unit do
sh "cd test; rake"
end

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

@ -6,6 +6,8 @@ module Functions
# A module for managing parser functions. Each specified function
# becomes an instance method on the Scope class.
@functions = {}
class << self
include Puppet::Util
end
@ -23,7 +25,6 @@ module Functions
# Create a new function type.
def self.newfunction(name, options = {}, &block)
@functions ||= {}
name = symbolize(name)
if @functions.include? name
@ -105,226 +106,15 @@ module Functions
return false
end
end
# Include the specified classes
newfunction(:include, :doc => "Evaluate one or more classes.") do |vals|
vals = [vals] unless vals.is_a?(Array)
# The 'false' disables lazy evaluation.
klasses = compiler.evaluate_classes(vals, self, false)
missing = vals.find_all do |klass|
! klasses.include?(klass)
end
unless missing.empty?
# Throw an error if we didn't evaluate all of the classes.
str = "Could not find class"
if missing.length > 1
str += "es"
end
str += " " + missing.join(", ")
if n = namespaces and ! n.empty? and n != [""]
str += " in namespaces %s" % @namespaces.join(", ")
end
self.fail Puppet::ParseError, str
end
end
# Tag the current scope with each passed name
newfunction(:tag, :doc => "Add the specified tags to the containing class
or definition. All contained objects will then acquire that tag, also.
") do |vals|
self.resource.tag(*vals)
end
# Test whether a given tag is set. This functions as a big OR -- if any of the
# specified tags are unset, we return false.
newfunction(:tagged, :type => :rvalue, :doc => "A boolean function that
tells you whether the current container is tagged with the specified tags.
The tags are ANDed, so that all of the specified tags must be included for
the function to return true.") do |vals|
configtags = compiler.catalog.tags
resourcetags = resource.tags
retval = true
vals.each do |val|
unless configtags.include?(val) or resourcetags.include?(val)
retval = false
break
end
end
return retval
end
# Test whether a given class or definition is defined
newfunction(:defined, :type => :rvalue, :doc => "Determine whether a given
type is defined, either as a native type or a defined type, or whether a class is defined.
This is useful for checking whether a class is defined and only including it if it is.
This function can also test whether a resource has been defined, using resource references
(e.g., ``if defined(File['/tmp/myfile']) { ... }``). This function is unfortunately
dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals|
result = false
vals.each do |val|
case val
when String:
# For some reason, it doesn't want me to return from here.
if Puppet::Type.type(val) or finddefine(val) or findclass(val)
result = true
break
end
when Puppet::Parser::Resource::Reference:
if findresource(val.to_s)
result = true
break
end
else
raise ArgumentError, "Invalid argument of type %s to 'defined'" % val.class
end
end
result
end
newfunction(:fqdn_rand, :type => :rvalue, :doc => "Generates random
numbers based on the node's fqdn. The first argument sets the range.
The second argument specifies a number to add to the seed and is
optional.") do |args|
require 'md5'
max = args[0]
if args[1] then
seed = args[1]
else
seed = 1
end
fqdn_seed = MD5.new(lookupvar('fqdn')).to_s.hex
srand(seed+fqdn_seed)
rand(max).to_s
end
newfunction(:fail, :doc => "Fail with a parse error.") do |vals|
vals = vals.collect { |s| s.to_s }.join(" ") if vals.is_a? Array
raise Puppet::ParseError, vals.to_s
end
# Runs a newfunction to create a function for each of the log levels
Puppet::Util::Log.levels.each do |level|
Puppet::Util::Log.levels.each do |level|
newfunction(level, :doc => "Log a message on the server at level
#{level.to_s}.") do |vals|
send(level, vals.join(" "))
end
end
newfunction(:template, :type => :rvalue, :doc => "Evaluate a template and
return its value. See `the templating docs </trac/puppet/wiki/PuppetTemplating>`_
for more information. Note that if multiple templates are specified, their
output is all concatenated and returned as the output of the function.
") do |vals|
require 'erb'
vals.collect do |file|
# Use a wrapper, so the template can't get access to the full
# Scope object.
debug "Retrieving template %s" % file
wrapper = Puppet::Parser::TemplateWrapper.new(self, file)
begin
wrapper.result()
rescue => detail
raise Puppet::ParseError,
"Failed to parse template %s: %s" %
[file, detail]
end
end.join("")
end
# This is just syntactic sugar for a collection, although it will generally
# be a good bit faster.
newfunction(:realize, :doc => "Make a virtual object real. This is useful
when you want to know the name of the virtual object and don't want to
bother with a full collection. It is slightly faster than a collection,
and, of course, is a bit shorter. You must pass the object using a
reference; e.g.: ``realize User[luke]``." ) do |vals|
coll = Puppet::Parser::Collector.new(self, :nomatter, nil, nil, :virtual)
vals = [vals] unless vals.is_a?(Array)
coll.resources = vals
compiler.add_collection(coll)
end
newfunction(:search, :doc => "Add another namespace for this class to search.
This allows you to create classes with sets of definitions and add
those classes to another class's search path.") do |vals|
vals.each do |val|
add_namespace(val)
#{level.to_s}.") do |vals|
send(level, vals.join(" "))
end
end
newfunction(:file, :type => :rvalue,
:doc => "Return the contents of a file. Multiple files
can be passed, and the first file that exists will be read in.") do |vals|
ret = nil
vals.each do |file|
unless file =~ /^#{File::SEPARATOR}/
raise Puppet::ParseError, "Files must be fully qualified"
end
if FileTest.exists?(file)
ret = File.read(file)
break
end
end
if ret
ret
else
raise Puppet::ParseError, "Could not find any files from %s" %
vals.join(", ")
end
end
newfunction(:generate, :type => :rvalue,
:doc => "Calls an external command and returns the results of the
command. Any arguments are passed to the external command as
arguments. If the generator does not exit with return code of 0,
the generator is considered to have failed and a parse error is
thrown. Generators can only have file separators, alphanumerics, dashes,
and periods in them. This function will attempt to protect you from
malicious generator calls (e.g., those with '..' in them), but it can
never be entirely safe. No subshell is used to execute
generators, so all shell metacharacters are passed directly to
the generator.") do |args|
unless args[0] =~ /^#{File::SEPARATOR}/
raise Puppet::ParseError, "Generators must be fully qualified"
end
unless args[0] =~ /^[-#{File::SEPARATOR}\w.]+$/
raise Puppet::ParseError,
"Generators can only contain alphanumerics, file separators, and dashes"
end
if args[0] =~ /\.\./
raise Puppet::ParseError,
"Can not use generators with '..' in them."
end
begin
output = Puppet::Util.execute(args)
rescue Puppet::ExecutionFailure => detail
raise Puppet::ParseError, "Failed to execute generator %s: %s" %
[args[0], detail]
end
output
end
newfunction(:sha1, :type => :rvalue,
:doc => "Returns a SHA1 hash value from a provided string.") do |args|
require 'sha1'
Digest::SHA1.hexdigest(args[0])
end
end
end

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

@ -0,0 +1,27 @@
# Test whether a given class or definition is defined
Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :doc => "Determine whether a given
type is defined, either as a native type or a defined type, or whether a class is defined.
This is useful for checking whether a class is defined and only including it if it is.
This function can also test whether a resource has been defined, using resource references
(e.g., ``if defined(File['/tmp/myfile']) { ... }``). This function is unfortunately
dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals|
result = false
vals.each do |val|
case val
when String:
# For some reason, it doesn't want me to return from here.
if Puppet::Type.type(val) or finddefine(val) or findclass(val)
result = true
break
end
when Puppet::Parser::Resource::Reference:
if findresource(val.to_s)
result = true
break
end
else
raise ArgumentError, "Invalid argument of type %s to 'defined'" % val.class
end
end
result
end

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

@ -0,0 +1,4 @@
Puppet::Parser::Functions::newfunction(:fail, :doc => "Fail with a parse error.") do |vals|
vals = vals.collect { |s| s.to_s }.join(" ") if vals.is_a? Array
raise Puppet::ParseError, vals.to_s
end

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

@ -0,0 +1,21 @@
# Returns the contents of a file
Puppet::Parser::Functions::newfunction(:file, :type => :rvalue,
:doc => "Return the contents of a file. Multiple files
can be passed, and the first file that exists will be read in.") do |vals|
ret = nil
vals.each do |file|
unless file =~ /^#{File::SEPARATOR}/
raise Puppet::ParseError, "Files must be fully qualified"
end
if FileTest.exists?(file)
ret = File.read(file)
break
end
end
if ret
ret
else
raise Puppet::ParseError, "Could not find any files from %s" %
vals.join(", ")
end
end

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

@ -0,0 +1,15 @@
Puppet::Parser::Functions::newfunction(:fqdn_rand, :type => :rvalue, :doc =>
"Generates random numbers based on the node's fqdn. The first argument
sets the range. The second argument specifies a number to add to the
seed and is optional.") do |args|
require 'md5'
max = args[0]
if args[1] then
seed = args[1]
else
seed = 1
end
fqdn_seed = MD5.new(lookupvar('fqdn')).to_s.hex
srand(seed+fqdn_seed)
rand(max).to_s
end

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

@ -0,0 +1,35 @@
# Runs an external command and returns the results
Puppet::Parser::Functions::newfunction(:generate, :type => :rvalue,
:doc => "Calls an external command and returns the results of the
command. Any arguments are passed to the external command as
arguments. If the generator does not exit with return code of 0,
the generator is considered to have failed and a parse error is
thrown. Generators can only have file separators, alphanumerics, dashes,
and periods in them. This function will attempt to protect you from
malicious generator calls (e.g., those with '..' in them), but it can
never be entirely safe. No subshell is used to execute
generators, so all shell metacharacters are passed directly to
the generator.") do |args|
unless args[0] =~ /^#{File::SEPARATOR}/
raise Puppet::ParseError, "Generators must be fully qualified"
end
unless args[0] =~ /^[-#{File::SEPARATOR}\w.]+$/
raise Puppet::ParseError,
"Generators can only contain alphanumerics, file separators, and dashes"
end
if args[0] =~ /\.\./
raise Puppet::ParseError,
"Can not use generators with '..' in them."
end
begin
output = Puppet::Util.execute(args)
rescue Puppet::ExecutionFailure => detail
raise Puppet::ParseError, "Failed to execute generator %s: %s" %
[args[0], detail]
end
output
end

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

@ -0,0 +1,26 @@
# Include the specified classes
Puppet::Parser::Functions::newfunction(:include, :doc => "Evaluate one or more classes.") do |vals|
vals = [vals] unless vals.is_a?(Array)
# The 'false' disables lazy evaluation.
klasses = compiler.evaluate_classes(vals, self, false)
missing = vals.find_all do |klass|
! klasses.include?(klass)
end
unless missing.empty?
# Throw an error if we didn't evaluate all of the classes.
str = "Could not find class"
if missing.length > 1
str += "es"
end
str += " " + missing.join(", ")
if n = namespaces and ! n.empty? and n != [""]
str += " in namespaces %s" % @namespaces.join(", ")
end
self.fail Puppet::ParseError, str
end
end

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

@ -0,0 +1,14 @@
# This is just syntactic sugar for a collection, although it will generally
# be a good bit faster.
Puppet::Parser::Functions::newfunction(:realize, :doc => "Make a virtual object real. This is useful
when you want to know the name of the virtual object and don't want to
bother with a full collection. It is slightly faster than a collection,
and, of course, is a bit shorter. You must pass the object using a
reference; e.g.: ``realize User[luke]``." ) do |vals|
coll = Puppet::Parser::Collector.new(self, :nomatter, nil, nil, :virtual)
vals = [vals] unless vals.is_a?(Array)
coll.resources = vals
compiler.add_collection(coll)
end

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

@ -0,0 +1,7 @@
Puppet::Parser::Functions::newfunction(:search, :doc => "Add another namespace for this class to search.
This allows you to create classes with sets of definitions and add
those classes to another class's search path.") do |vals|
vals.each do |val|
add_namespace(val)
end
end

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

@ -0,0 +1,6 @@
Puppet::Parser::Functions::newfunction(:sha1, :type => :rvalue,
:doc => "Returns a SHA1 hash value from a provided string.") do |args|
require 'sha1'
Digest::SHA1.hexdigest(args[0])
end

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

@ -0,0 +1,6 @@
# Tag the current scope with each passed name
Puppet::Parser::Functions::newfunction(:tag, :doc => "Add the specified tags to the containing class
or definition. All contained objects will then acquire that tag, also.
") do |vals|
self.resource.tag(*vals)
end

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

@ -0,0 +1,18 @@
# Test whether a given tag is set. This functions as a big OR -- if any of the specified tags are unset, we return false.
Puppet::Parser::Functions::newfunction(:tagged, :type => :rvalue, :doc => "A boolean function that
tells you whether the current container is tagged with the specified tags.
The tags are ANDed, so that all of the specified tags must be included for
the function to return true.") do |vals|
configtags = compiler.catalog.tags
resourcetags = resource.tags
retval = true
vals.each do |val|
unless configtags.include?(val) or resourcetags.include?(val)
retval = false
break
end
end
return retval
end

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

@ -0,0 +1,22 @@
Puppet::Parser::Functions::newfunction(:template, :type => :rvalue, :doc =>
"Evaluate a template and return its value. See `the templating docs
</trac/puppet/wiki/PuppetTemplating>`_ for more information. Note that
if multiple templates are specified, their output is all concatenated
and returned as the output of the function.") do |vals|
require 'erb'
vals.collect do |file|
# Use a wrapper, so the template can't get access to the full
# Scope object.
debug "Retrieving template %s" % file
wrapper = Puppet::Parser::TemplateWrapper.new(self, file)
begin
wrapper.result()
rescue => detail
raise Puppet::ParseError,
"Failed to parse template %s: %s" %
[file, detail]
end
end.join("")
end

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

@ -95,11 +95,10 @@ class Puppet::Parser::Parser
raise Puppet::Error, "Could not find file %s" % file
end
end
if @files.detect { |f| f.file == file }
raise Puppet::AlreadyImportedError.new("Import loop detected")
else
@files << Puppet::Util::LoadedFile.new(file)
if check_and_add_to_watched_files(file)
@lexer.file = file
else
raise Puppet::AlreadyImportedError.new("Import loop detected")
end
end
@ -216,7 +215,7 @@ class Puppet::Parser::Parser
# Initialize or reset all of our variables.
def initvars
@lexer = Puppet::Parser::Lexer.new()
@files = []
@files = {}
@loaded = []
end
@ -435,8 +434,8 @@ class Puppet::Parser::Parser
# See if any of the files have changed.
def reparse?
if file = @files.detect { |file| file.changed? }
return file.stamp
if file = @files.detect { |name, file| file.changed? }
return file[1].stamp
else
return false
end
@ -449,12 +448,18 @@ class Puppet::Parser::Parser
# Add a new file to be checked when we're checking to see if we should be
# reparsed. This is basically only used by the TemplateWrapper to let the
# parser know about templates that should be parsed.
def watch_file(*files)
files.each do |file|
unless file.is_a? Puppet::Util::LoadedFile
file = Puppet::Util::LoadedFile.new(file)
end
@files << file
def watch_file(filename)
check_and_add_to_watched_files(filename)
end
private
def check_and_add_to_watched_files(filename)
unless @files.include?(filename)
@files[filename] = Puppet::Util::LoadedFile.new(filename)
return true
else
return false
end
end
end

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

@ -25,7 +25,7 @@ class Puppet::Provider::Confine
begin
require "puppet/provider/confine/%s" % name
rescue LoadError => detail
unless detail.to_s.include?("No such file")
unless detail.to_s =~ /No such file/i
warn "Could not load confine test '%s': %s" % [name, detail]
end
# Could not find file

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

@ -0,0 +1,17 @@
class RemoveDuplicatedIndexOnAllTables < ActiveRecord::Migration
def self.up
ActiveRecord::Base.connection.tables.each do |t|
if ActiveRecord::Base.connection.indexes(t).collect {|c| c.columns}.include?("id")
remove_index t.to_s, :id
end
end
end
def self.down
ActiveRecord::Base.connection.tables.each do |t|
unless ActiveRecord::Base.connection.indexes(t).collect {|c| c.columns}.include?("id")
add_index t.to_s, :id, :integer => true
end
end
end
end

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

@ -16,7 +16,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :resources, :id, :integer => true
add_index :resources, :host_id, :integer => true
add_index :resources, :source_file_id, :integer => true
@ -34,7 +33,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :source_files, :id, :integer => true
add_index :source_files, :filename
create_table :resource_tags do |t|
@ -43,7 +41,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :resource_tags, :id, :integer => true
add_index :resource_tags, :resource_id, :integer => true
add_index :resource_tags, :puppet_tag_id, :integer => true
@ -65,7 +62,6 @@ class Puppet::Rails::Schema
t.column :source_file_id, :integer
t.column :created_at, :datetime
end
add_index :hosts, :id, :integer => true
add_index :hosts, :source_file_id, :integer => true
add_index :hosts, :name
@ -74,7 +70,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :fact_names, :id, :integer => true
add_index :fact_names, :name
create_table :fact_values do |t|
@ -84,7 +79,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :fact_values, :id, :integer => true
add_index :fact_values, :fact_name_id, :integer => true
add_index :fact_values, :host_id, :integer => true
@ -96,7 +90,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :param_values, :id, :integer => true
add_index :param_values, :param_name_id, :integer => true
add_index :param_values, :resource_id, :integer => true
@ -105,7 +98,6 @@ class Puppet::Rails::Schema
t.column :updated_at, :datetime
t.column :created_at, :datetime
end
add_index :param_names, :id, :integer => true
add_index :param_names, :name
end
end

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

@ -1197,5 +1197,13 @@ file { "/tmp/yayness":
parser.import("test")
end
end
def test_watch_file_only_once
FileTest.stubs(:exists?).returns(true)
parser = mkparser
parser.watch_file("doh")
parser.watch_file("doh")
assert_equal(1, parser.files.select { |name, file| file.file == "doh" }.length, "Length of watched 'doh' files was not 1")
end
end