Conflicts:
	lib/puppet/indirector/facts/facter.rb
	lib/puppet/provider/augeas/augeas.rb
	lib/puppet/util/filetype.rb
	spec/unit/indirector/facts/facter.rb
	spec/unit/provider/augeas/augeas.rb
	test/util/filetype.rb
This commit is contained in:
Luke Kanies 2009-02-13 18:24:34 -06:00
Родитель a2270b4a4f 2561c8e252
Коммит 3fbec12076
25 изменённых файлов: 856 добавлений и 344 удалений

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

@ -14,6 +14,22 @@
Fixed #1840 - Bug fixes and improvements for Emacs puppet-mode.el
0.24.8
Fixed #1831 - Added sprintf function
Fixed #1830 - Added regsubst function
Updated up2date and service confines to add support for Oracle EL and VM
Fixing #1948 and #1953 - augeas ins bug: wrong number of arguments (1 for 3)
Fixing #944 - changing error message from warning to info - connection recycled
Fixed #961 - puppetd creating too many/not closing TCP connections
Fixed #1959 - Added column protection for environment schema migration
Fixing #1869 - autoloaded files should never leak exceptions
Fixing #1543 - Nagios parse errors no longer kill Puppet
Fixed #1420 - nagios_serviceescalation not allowing host_name more than one type

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

@ -16,7 +16,7 @@ elseif exists("b:current_syntax")
finish
endif
syn region puppetDefine start="^\s*\(class\|define\|site\|node\)" end="{" contains=puppetDefType,puppetDefName,puppetDefArguments
syn region puppetDefine start="^\s*\(class\|define\|node\)" end="{" contains=puppetDefType,puppetDefName,puppetDefArguments
syn keyword puppetDefType class define site node inherits contained
syn keyword puppetInherits inherits contained
syn region puppetDefArguments start="(" end=")" contains=puppetArgument

25
lib/puppet/external/nagios/base.rb поставляемый
Просмотреть файл

@ -416,18 +416,20 @@ class Nagios::Base
:dependent_service_description, :host_name, :hostgroup_name,
:service_description, :inherits_parent, :execution_failure_criteria,
:notification_failure_criteria, :dependency_period,
:register, :use
:register, :use,
:_naginator_name
setnamevar :service_description
setnamevar :_naginator_name
end
newtype :serviceescalation do
setparameters :host_name, :hostgroup_name, :service_description, :contacts,
:contact_groups, :first_notification, :last_notification,
:notification_interval, :escalation_period, :escalation_options,
:register, :use
:register, :use,
:_naginator_name
setnamevar :service_description
setnamevar :_naginator_name
end
newtype :hostdependency do
@ -435,18 +437,20 @@ class Nagios::Base
setparameters :dependent_host_name, :dependent_hostgroup_name, :host_name,
:hostgroup_name, :inherits_parent, :execution_failure_criteria,
:notification_failure_criteria, :dependency_period,
:register, :use
:register, :use,
:_naginator_name
setnamevar :host_name
setnamevar :_naginator_name
end
newtype :hostescalation do
setparameters :host_name, :hostgroup_name, :contacts, :contact_groups,
:first_notification, :last_notification, :notification_interval,
:escalation_period, :escalation_options,
:register, :use
:register, :use,
:_naginator_name
setnamevar :host_name
setnamevar :_naginator_name
end
newtype :hostextinfo do
@ -463,9 +467,10 @@ class Nagios::Base
setparameters :host_name, :service_description, :notes, :notes_url,
:action_url, :icon_image, :icon_image_alt,
:register, :use
:register, :use,
:_naginator_name
setnamevar :service_description
setnamevar :_naginator_name
end
end

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

@ -8,8 +8,12 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
def self.load_fact_plugins
# LAK:NOTE See http://snurl.com/21zf8 [groups_google_com]
x = Puppet[:factpath].split(":").each do |dir|
# Add any per-module fact directories to the factpath
module_fact_dirs = Puppet[:modulepath].split(":").collect do |d|
Dir.glob("%s/*/plugins/facter" % d)
end.flatten
dirs = module_fact_dirs + Puppet[:factpath].split(":")
x = dirs.each do |dir|
load_facts_in_dir(dir)
end
end

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

@ -36,57 +36,7 @@ module Puppet::Network
interface.methods.each { |ary|
method = ary[0]
newclient.send(:define_method,method) { |*args|
Puppet.debug "Calling %s.%s" % [namespace, method]
begin
call("%s.%s" % [namespace, method.to_s],*args)
rescue OpenSSL::SSL::SSLError => detail
if detail.message =~ /bad write retry/
Puppet.warning "Transient SSL write error; restarting connection and retrying"
self.recycle_connection
retry
end
["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
if detail.message.include?(str)
Puppet.warning "Certificate validation failed; consider using the certname configuration option"
end
end
raise XMLRPCClientError,
"Certificates were not trusted: %s" % detail
rescue ::XMLRPC::FaultException => detail
raise XMLRPCClientError, detail.faultString
rescue Errno::ECONNREFUSED => detail
msg = "Could not connect to %s on port %s" %
[@host, @port]
raise XMLRPCClientError, msg
rescue SocketError => detail
Puppet.err "Could not find server %s: %s" %
[@host, detail.to_s]
error = XMLRPCClientError.new(
"Could not find server %s" % @host
)
error.set_backtrace detail.backtrace
raise error
rescue Errno::EPIPE, EOFError
Puppet.warning "Other end went away; restarting connection and retrying"
self.recycle_connection
retry
rescue Timeout::Error => detail
Puppet.err "Connection timeout calling %s.%s: %s" %
[namespace, method, detail.to_s]
error = XMLRPCClientError.new("Connection Timeout")
error.set_backtrace(detail.backtrace)
raise error
rescue => detail
if detail.message =~ /^Wrong size\. Was \d+, should be \d+$/
Puppet.warning "XMLRPC returned wrong size. Retrying."
retry
end
Puppet.err "Could not call %s.%s: %s" %
[namespace, method, detail.inspect]
error = XMLRPCClientError.new(detail.to_s)
error.set_backtrace detail.backtrace
raise error
end
make_rpc_call(namespace, method, *args)
}
}
@ -97,13 +47,119 @@ module Puppet::Network
@clients[handler] || self.mkclient(handler)
end
class ErrorHandler
def initialize(&block)
metaclass.define_method(:execute, &block)
end
end
# Use a class variable so all subclasses have access to it.
@@error_handlers = {}
def self.error_handler(exception)
if handler = @@error_handlers[exception.class]
return handler
else
return @@error_handlers[:default]
end
end
def self.handle_error(*exceptions, &block)
handler = ErrorHandler.new(&block)
exceptions.each do |exception|
@@error_handlers[exception] = handler
end
end
handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
if detail.message =~ /bad write retry/
Puppet.warning "Transient SSL write error; restarting connection and retrying"
client.recycle_connection
return :retry
end
["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
if detail.message.include?(str)
Puppet.warning "Certificate validation failed; consider using the certname configuration option"
end
end
raise XMLRPCClientError, "Certificates were not trusted: %s" % detail
end
handle_error(:default) do |client, detail, namespace, method|
if detail.message.to_s =~ /^Wrong size\. Was \d+, should be \d+$/
Puppet.warning "XMLRPC returned wrong size. Retrying."
return :retry
end
Puppet.err "Could not call %s.%s: %s" % [namespace, method, detail.inspect]
error = XMLRPCClientError.new(detail.to_s)
error.set_backtrace detail.backtrace
raise error
end
handle_error(OpenSSL::SSL::SSLError) do |client, detail, namespace, method|
if detail.message =~ /bad write retry/
Puppet.warning "Transient SSL write error; restarting connection and retrying"
client.recycle_connection
return :retry
end
["certificate verify failed", "hostname was not match", "hostname not match"].each do |str|
if detail.message.include?(str)
Puppet.warning "Certificate validation failed; consider using the certname configuration option"
end
end
raise XMLRPCClientError, "Certificates were not trusted: %s" % detail
end
handle_error(::XMLRPC::FaultException) do |client, detail, namespace, method|
raise XMLRPCClientError, detail.faultString
end
handle_error(Errno::ECONNREFUSED) do |client, detail, namespace, method|
msg = "Could not connect to %s on port %s" % [client.host, client.port]
raise XMLRPCClientError, msg
end
handle_error(SocketError) do |client, detail, namespace, method|
Puppet.err "Could not find server %s: %s" % [@host, detail.to_s]
error = XMLRPCClientError.new("Could not find server %s" % client.host)
error.set_backtrace detail.backtrace
raise error
end
handle_error(Errno::EPIPE, EOFError) do |client, detail, namespace, method|
Puppet.info "Other end went away; restarting connection and retrying"
client.recycle_connection
return :retry
end
handle_error(Timeout::Error) do |client, detail, namespace, method|
Puppet.err "Connection timeout calling %s.%s: %s" % [namespace, method, detail.to_s]
error = XMLRPCClientError.new("Connection Timeout")
error.set_backtrace(detail.backtrace)
raise error
end
def make_rpc_call(namespace, method, *args)
Puppet.debug "Calling %s.%s" % [namespace, method]
begin
call("%s.%s" % [namespace, method.to_s],*args)
rescue Exception => detail
retry if self.class.error_handler(detail).execute(self, detail, namespace, method) == :retry
end
ensure
http.finish if http.started?
end
def http
unless @http
@http = Puppet::Network::HttpPool.http_instance(@host, @port, true)
@http = Puppet::Network::HttpPool.http_instance(host, port, true)
end
@http
end
attr_reader :host, :port
def initialize(hash = {})
hash[:Path] ||= "/RPC2"
hash[:Server] ||= Puppet[:server]
@ -135,7 +191,11 @@ module Puppet::Network
# or we've just downloaded certs and we need to create new http instances
# with the certs added.
def recycle_connection
@http = Puppet::Network::HttpPool.http_instance(@host, @port, true) # reset the instance
if http.started?
http.finish
end
@http = nil
self.http # force a new one
end
def start

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

@ -0,0 +1,93 @@
module Puppet::Parser::Functions
newfunction(:regsubst, :type => :rvalue,
:doc => "\
Perform regexp replacement on a string.
Parameters (in order):
:str:
The string to operate on.
:regexp:
The regular expression matching the string. If you want it
anchored at the start and/or end of the string, you must do
that with ^ and $ yourself.
:replacement:
Replacement string. Can contain back references to what was
matched using \\0, \\1, and so on.
:flags:
Optional. String of single letter flags for how the regexp
is interpreted:
- **E**
Extended regexps
- **I**
Ignore case in regexps
- **M**
Multiline regexps
- **G**
Global replacement; all occurances of the regexp in
the string will be replaced. Without this, only the
first occurance will be replaced.
:lang:
Optional. How to handle multibyte characters. A
single-character string with the following values:
- **N**
None
- **E**
EUC
- **S**
SJIS
- **U**
UTF-8
**Examples**
Get the third octet from the node's IP address: ::
$i3 = regsubst($ipaddress,
'^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$',
'\\\\3')
Put angle brackets around each octet in the node's IP address: ::
$x = regsubst($ipaddress, '([0-9]+)', '<\\\\1>', 'G')
") \
do |args|
flag_mapping = {
"E" => Regexp::EXTENDED,
"I" => Regexp::IGNORECASE,
"M" => Regexp::MULTILINE,
}
if args.length < 3 or args.length > 5
raise Puppet::ParseError, ("regsub(): wrong number of arguments" +
" (#{args.length}; min 3, max 5)")
end
str, regexp, replacement, flags, lang = args
reflags = 0
global = false
(flags or "").each_byte do |f|
f = f.chr
if f == "G"
global = true
else
fvalue = flag_mapping[f]
if !fvalue
raise Puppet::ParseError, "regsub(): bad flag `#{f}'"
end
reflags |= fvalue
end
end
re = Regexp.compile(regexp, reflags, lang)
if global
result = str.gsub(re, replacement)
else
result = str.sub(re, replacement)
end
return result
end
end

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

@ -0,0 +1,17 @@
module Puppet::Parser::Functions
newfunction(:sprintf, :type => :rvalue,
:doc => "\
Perform printf-style formatting of text.
The first parameter is format string describing how the rest of the
parameters should be formatted. See the documentation for the
``Kernel::sprintf()`` function in Ruby for all the details.
") \
do |args|
if args.length < 1
raise Puppet::ParseError, 'sprintf() needs at least one argument'
end
fmt = args.shift()
return sprintf(fmt, *args)
end
end

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

@ -69,7 +69,6 @@ Puppet::Type.type(:augeas).provide(:augeas) do
commands.concat(parse_commands(datum))
end
end
return commands
end
@ -181,15 +180,36 @@ Puppet::Type.type(:augeas).provide(:augeas) do
fail("invalid command #{cmd_array.join[" "]}") if cmd_array.length < 2
command = cmd_array[0]
cmd_array.shift()
loc = cmd_array[0]
cmd_array[0]=File.join(context, loc)
debug("sending command '#{command}' with params #{cmd_array.inspect}")
begin
case command
when "set": aug.set(cmd_array[0], cmd_array[1])
when "rm", "remove": aug.rm(cmd_array[0])
when "clear": aug.clear(cmd_array[0])
when "insert", "ins": aug.insert(cmd_array[0])
when "set":
cmd_array[0]=File.join(context, cmd_array[0])
debug("sending command '#{command}' with params #{cmd_array.inspect}")
aug.set(cmd_array[0], cmd_array[1])
when "rm", "remove":
cmd_array[0]=File.join(context, cmd_array[0])
debug("sending command '#{command}' with params #{cmd_array.inspect}")
aug.rm(cmd_array[0])
when "clear":
cmd_array[0]=File.join(context, cmd_array[0])
debug("sending command '#{command}' with params #{cmd_array.inspect}")
aug.clear(cmd_array[0])
when "insert", "ins"
ext_array = cmd_array[1].split(" ") ;
if cmd_array.size < 2 or ext_array.size < 2
fail("ins requires 3 parameters")
end
label = cmd_array[0]
where = ext_array[0]
path = File.join(context, ext_array[1])
case where
when "before": before = true
when "after": before = false
else fail("Invalid value '#{where}' for where param")
end
debug("sending command '#{command}' with params #{[label, where, path].inspect()}")
aug.insert(path, label, before)
else fail("Command '#{command}' is not supported")
end
rescue Exception => e

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

@ -7,6 +7,7 @@ require 'puppet/external/nagios'
# The base class for all Naginator providers.
class Puppet::Provider::Naginator < Puppet::Provider::ParsedFile
NAME_STRING = "## --PUPPET_NAME-- (called '_naginator_name' in the manifest)"
# Retrieve the associated class from Nagios::Base.
def self.nagios_type
unless defined?(@nagios_type) and @nagios_type
@ -24,14 +25,14 @@ class Puppet::Provider::Naginator < Puppet::Provider::ParsedFile
def self.parse(text)
begin
Nagios::Parser.new.parse(text)
Nagios::Parser.new.parse(text.gsub(NAME_STRING, "_naginator_name"))
rescue => detail
raise Puppet::Error, "Could not parse configuration for %s: %s" % [resource_type.name, detail]
end
end
def self.to_file(records)
header + records.collect { |record| record.to_s }.join("\n")
header + records.collect { |record| record.to_s }.join("\n").gsub("_naginator_name", NAME_STRING)
end
def self.skip_record?(record)

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

@ -3,9 +3,11 @@ Puppet::Type.type(:package).provide :up2date, :parent => :rpm, :source => :rpm d
mechanism."
commands :up2date => "/usr/sbin/up2date-nox"
defaultfor :operatingsystem => :redhat,
defaultfor :operatingsystem => [:redhat, :oel, :ovm]
:lsbdistrelease => ["2.1", "3", "4"]
confine :operatingsystem => :redhat
confine :operatingsystem => [:redhat, :oel, :ovm]
# Install a package using 'up2date'.
def install

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

@ -78,8 +78,22 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
@modified.reject! { |t| flushed.include?(t) }
end
# Make sure our file is backed up, but only back it up once per transaction.
# We cheat and rely on the fact that @records is created on each prefetch.
def self.backup_target(target)
unless defined?(@backup_stats)
@backup_stats = {}
end
return nil if @backup_stats[target] == @records.object_id
target_object(target).backup
@backup_stats[target] = @records.object_id
end
# Flush all of the records relating to a specific target.
def self.flush_target(target)
backup_target(target)
records = target_records(target).reject { |r|
r[:ensure] == :absent
}

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

@ -9,7 +9,7 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do
commands :chkconfig => "/sbin/chkconfig", :service => "/sbin/service"
defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos, :sles]
defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos, :sles, :oel, :ovm]
def self.defpath
superclass.defpath

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

@ -1,9 +1,13 @@
class AddEnvironmentToHost < ActiveRecord::Migration
def self.up
add_column :hosts, :environment, :string
unless ActiveRecord::Base.connection.columns(:hosts).collect {|c| c.name}.include?("environment")
add_column :hosts, :environment, :string
end
end
def self.down
remove_column :hosts, :environment
if ActiveRecord::Base.connection.columns(:hosts).collect {|c| c.name}.include?("environment")
remove_column :hosts, :environment
end
end
end

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

@ -92,10 +92,12 @@ Puppet::Type.newtype(:augeas) do
rm [PATH] Removes the node at location PATH
remove [PATH] Synonym for rm
clear [PATH] Keeps the node at PATH, but removes the value.
ins [PATH] Inserts an empty node at PATH.
insert [PATH] Synonym for ins
ins [LABEL] [WHERE] [PATH]
Inserts an empty node LABEL either [WHERE={before|after}] PATH.
insert [LABEL] [WHERE] [PATH]
Synonym for ins
If the parameter 'context' is set that that value is prepended to PATH"
If the parameter 'context' is set that value is prepended to PATH"
munge do |value|
provider.parse_commands(value)

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

@ -78,7 +78,7 @@ class Puppet::Util::Autoload
name = symbolize(name)
loaded name, file
return true
rescue LoadError => detail
rescue Exception => detail
# I have no idea what's going on here, but different versions
# of ruby are raising different errors on missing files.
unless detail.to_s =~ /^no such file/i
@ -115,7 +115,7 @@ class Puppet::Util::Autoload
begin
Kernel.require file
loaded(name, file)
rescue => detail
rescue Exception => detail
if Puppet[:trace]
puts detail.backtrace
end

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

@ -74,7 +74,7 @@ class Puppet::Util::FileType
# Back the file up before replacing it.
def backup
bucket.backup(@path) if FileTest.exists?(@path)
bucket.backup(@path) if File.exists?(@path)
end
# Pick or create a filebucket to use.
@ -94,7 +94,7 @@ class Puppet::Util::FileType
newfiletype(:flat) do
# Read the file.
def read
if File.exists?(@path)
if File.exist?(@path)
File.read(@path)
else
return nil
@ -103,17 +103,13 @@ class Puppet::Util::FileType
# Remove the file.
def remove
if File.exists?(@path)
if File.exist?(@path)
File.unlink(@path)
end
end
# Overwrite the file.
def write(text)
backup()
raise("Cannot create file %s in absent directory" % @path) unless FileTest.exist?(File.dirname(@path))
require "tempfile"
tf = Tempfile.new("puppet")
tf.print text; tf.flush
@ -255,92 +251,4 @@ class Puppet::Util::FileType
output_file.delete
end
end
# Treat netinfo tables as a single file, just for simplicity of certain
# types
newfiletype(:netinfo) do
class << self
attr_accessor :format
end
def read
%x{nidump -r /#{@path} /}
end
# This really only makes sense for cron tabs.
def remove
%x{nireport / /#{@path} name}.split("\n").each do |name|
newname = name.gsub(/\//, '\/').sub(/\s+$/, '')
output = %x{niutil -destroy / '/#{@path}/#{newname}'}
unless $? == 0
raise Puppet::Error, "Could not remove %s from %s" %
[name, @path]
end
end
end
# Convert our table to an array of hashes. This only works for
# handling one table at a time.
def to_array(text = nil)
unless text
text = read
end
hash = nil
# Initialize it with the first record
records = []
text.split("\n").each do |line|
next if line =~ /^[{}]$/ # Skip the wrapping lines
next if line =~ /"name" = \( "#{@path}" \)/ # Skip the table name
next if line =~ /CHILDREN = \(/ # Skip this header
next if line =~ /^ \)/ # and its closer
# Now we should have nothing but records, wrapped in braces
case line
when /^\s+\{/: hash = {}
when /^\s+\}/: records << hash
when /\s+"(\w+)" = \( (.+) \)/
field = $1
values = $2
# Always use an array
hash[field] = []
values.split(/, /).each do |value|
if value =~ /^"(.*)"$/
hash[field] << $1
else
raise ArgumentError, "Could not match value %s" % value
end
end
else
raise ArgumentError, "Could not match line %s" % line
end
end
records
end
def write(text)
text.gsub!(/^#.*\n/,'')
text.gsub!(/^$/,'')
if text == "" or text == "\n"
self.remove
return
end
unless format = self.class.format
raise Puppe::DevError, "You must define the NetInfo format to inport"
end
IO.popen("niload -d #{format} . 1>/dev/null 2>/dev/null", "w") { |p|
p.print text
}
unless $? == 0
raise ArgumentError, "Failed to write %s" % @path
end
end
end
end

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

@ -93,15 +93,6 @@ describe Puppet::Node::Facts::Facter do
end
end
it "should load each directory in the Fact path when loading fact plugins" do
Puppet.settings.expects(:value).with(:factpath).returns("one%stwo" % File::PATH_SEPARATOR)
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two")
Puppet::Node::Facts::Facter.load_fact_plugins
end
it "should skip files when asked to load a directory" do
FileTest.expects(:directory?).with("myfile").returns false
@ -119,4 +110,33 @@ describe Puppet::Node::Facts::Facter do
Puppet::Node::Facts::Facter.load_facts_in_dir("mydir")
end
describe Puppet::Node::Facts::Facter, "when loading fact plugins from disk" do
it "should load each directory in the Fact path" do
Puppet.settings.stubs(:value).returns "foo"
Puppet.settings.expects(:value).with(:factpath).returns("one%stwo" % File::PATH_SEPARATOR)
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two")
Puppet::Node::Facts::Facter.load_fact_plugins
end
it "should load all facts from the modules" do
Puppet.settings.stubs(:value).returns "foo"
Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir)
Puppet.settings.expects(:value).with(:modulepath).returns("one%stwo" % File::PATH_SEPARATOR)
Dir.expects(:glob).with("one/*/plugins/facter").returns %w{oneA oneB}
Dir.expects(:glob).with("two/*/plugins/facter").returns %w{twoA twoB}
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA")
Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB")
Puppet::Node::Facts::Facter.load_fact_plugins
end
end
end

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

@ -2,12 +2,170 @@
Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
describe Puppet::Network do
it "should raise an XMLRPCClientError if a generated class raises a Timeout::Error" do
http = mock 'http'
Puppet::Network::HttpPool.stubs(:http_instance).returns http
file = Puppet::Network::Client.file.new({:Server => "foo.com"})
http.stubs(:post2).raises Timeout::Error
lambda { file.retrieve }.should raise_error(Puppet::Network::XMLRPCClientError)
describe Puppet::Network::XMLRPCClient do
describe "when performing the rpc call" do
before do
@client = Puppet::Network::Client.report.xmlrpc_client.new
@client.stubs(:call).returns "foo"
end
it "should call the specified namespace and method, with the specified arguments" do
@client.expects(:call).with("puppetreports.report", "eh").returns "foo"
@client.report("eh")
end
it "should return the results from the call" do
@client.expects(:call).returns "foo"
@client.report("eh").should == "foo"
end
it "should always close the http connection if it is still open after the call" do
http = mock 'http'
@client.stubs(:http).returns http
http.expects(:started?).returns true
http.expects(:finish)
@client.report("eh").should == "foo"
end
it "should always close the http connection if it is still open after a call that raises an exception" do
http = mock 'http'
@client.stubs(:http).returns http
@client.expects(:call).raises RuntimeError
http.expects(:started?).returns true
http.expects(:finish)
lambda { @client.report("eh") }.should raise_error
end
describe "when returning the http instance" do
it "should use the http pool to create the instance" do
@client.instance_variable_set("@http", nil)
@client.expects(:host).returns "myhost"
@client.expects(:port).returns "myport"
Puppet::Network::HttpPool.expects(:http_instance).with("myhost", "myport", true).returns "http"
@client.http.should == "http"
end
it "should reuse existing instances" do
@client.http.should equal(@client.http)
end
end
describe "when recycling the connection" do
it "should close the existing instance if it's open" do
http = mock 'http'
@client.stubs(:http).returns http
http.expects(:started?).returns true
http.expects(:finish)
@client.recycle_connection
end
it "should force creation of a new instance" do
Puppet::Network::HttpPool.expects(:http_instance).returns "second_http"
@client.recycle_connection
@client.http.should == "second_http"
end
end
describe "and an exception is raised" do
it "should raise XMLRPCClientError if XMLRPC::FaultException is raised" do
error = XMLRPC::FaultException.new("foo", "bar")
@client.expects(:call).raises(error)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should raise XMLRPCClientError if Errno::ECONNREFUSED is raised" do
@client.expects(:call).raises(Errno::ECONNREFUSED)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should log and raise XMLRPCClientError if Timeout::Error is raised" do
Puppet.expects(:err)
@client.expects(:call).raises(Timeout::Error)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should log and raise XMLRPCClientError if SocketError is raised" do
Puppet.expects(:err)
@client.expects(:call).raises(SocketError)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should log, recycle the connection, and retry if Errno::EPIPE is raised" do
@client.expects(:call).times(2).raises(Errno::EPIPE).then.returns "eh"
Puppet.expects(:warning)
@client.expects(:recycle_connection)
@client.report("eh")
end
it "should log, recycle the connection, and retry if EOFError is raised" do
@client.expects(:call).times(2).raises(EOFError).then.returns "eh"
Puppet.expects(:warning)
@client.expects(:recycle_connection)
@client.report("eh")
end
it "should log and retry if an exception containing 'Wrong size' is raised" do
error = RuntimeError.new("Wrong size. Was 15, should be 30")
@client.expects(:call).times(2).raises(error).then.returns "eh"
Puppet.expects(:warning)
@client.report("eh")
end
it "should raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised" do
@client.expects(:call).raises(OpenSSL::SSL::SSLError)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should log and raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised with certificate issues" do
error = OpenSSL::SSL::SSLError.new("hostname was not match")
@client.expects(:call).raises(error)
Puppet.expects(:warning)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
it "should log, recycle the connection, and retry if OpenSSL::SSL::SSLError is raised containing 'bad write retry'" do
error = OpenSSL::SSL::SSLError.new("bad write retry")
@client.expects(:call).times(2).raises(error).then.returns "eh"
@client.expects(:recycle_connection)
Puppet.expects(:warning)
@client.report("eh")
end
it "should log and raise XMLRPCClientError if any other exception is raised" do
@client.expects(:call).raises(RuntimeError)
Puppet.expects(:err)
lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
end
end
end
end

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

@ -0,0 +1,88 @@
#! /usr/bin/env ruby
require File.dirname(__FILE__) + '/../../../spec_helper'
describe "the regsubst function" do
before :each do
@scope = Puppet::Parser::Scope.new()
end
it "should exist" do
Puppet::Parser::Functions.function("regsubst").should == "function_regsubst"
end
it "should raise a ParseError if there is less than 3 arguments" do
lambda { @scope.function_regsubst(["foo", "bar"]) }.should(
raise_error(Puppet::ParseError))
end
it "should raise a ParseError if there is more than 5 arguments" do
lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "del", "x", "y"]) }.should(
raise_error(Puppet::ParseError))
end
it "should raise a ParseError when given a bad flag" do
lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "X"]) }.should(
raise_error(Puppet::ParseError))
end
it "should handle groups" do
result = @scope.function_regsubst(
[ '130.236.254.10',
'^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$',
'\4-\3-\2-\1'
])
result.should(eql("10-254-236-130"))
end
it "should handle simple regexps" do
result = @scope.function_regsubst(
[ "the monkey breaks banana trees",
"b[an]*a",
"coconut"
])
result.should(eql("the monkey breaks coconut trees"))
end
it "should handle case-sensitive regexps" do
result = @scope.function_regsubst(
[ "the monkey breaks baNAna trees",
"b[an]+a",
"coconut"
])
result.should(eql("the monkey breaks baNAna trees"))
end
it "should handle case-insensitive regexps" do
result = @scope.function_regsubst(
[ "the monkey breaks baNAna trees",
"b[an]+a",
"coconut",
"I"
])
result.should(eql("the monkey breaks coconut trees"))
end
it "should handle global substitutions" do
result = @scope.function_regsubst(
[ "the monkey breaks\tbanana trees",
"[ \t]",
"--",
"G"
])
result.should(eql("the--monkey--breaks--banana--trees"))
end
it "should handle global substitutions with groups" do
result = @scope.function_regsubst(
[ '130.236.254.10',
'([0-9]+)',
'<\1>',
'G'
])
result.should(eql('<130>.<236>.<254>.<10>'))
end
end

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

@ -0,0 +1,42 @@
#! /usr/bin/env ruby
require File.dirname(__FILE__) + '/../../../spec_helper'
describe "the sprintf function" do
before :each do
@scope = Puppet::Parser::Scope.new()
end
it "should exist" do
Puppet::Parser::Functions.function("sprintf").should == "function_sprintf"
end
it "should raise a ParseError if there is less than 1 argument" do
lambda { @scope.function_sprintf([]) }.should(
raise_error(Puppet::ParseError))
end
it "should format integers" do
result = @scope.function_sprintf(["%+05d", "23"])
result.should(eql("+0023"))
end
it "should format floats" do
result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"])
result.should(eql("+2.72"))
end
it "should format large floats" do
result = @scope.function_sprintf(["%+.2e", "27182818284590451"])
result.should(eql("+2.72e+16"))
end
it "should perform more complex formatting" do
result = @scope.function_sprintf(
[ "<%.8s:%#5o %#8X (%-8s)>",
"overlongstring", "23", "48879", "foo" ])
result.should(eql("<overlong: 027 0XBEEF (foo )>"))
end
end

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

@ -285,32 +285,42 @@ describe provider_class do
@augeas.expects(:clear).with("/foo/Jar/Jar")
@augeas.expects(:save).returns(true)
@provider.execute_changes.should == :executed
end
end
it "should handle insert commands" do
command = [["insert", "/Jar/Jar"]]
it "should handle ins commands with before" do
command = [["ins", "Binks", "before /Jar/Jar"]]
context = "/foo"
@resource.expects(:[]).times(2).returns(command).then.returns(context)
@augeas.expects(:insert).with("/foo/Jar/Jar")
@augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true)
@augeas.expects(:save).returns(true)
@provider.execute_changes.should == :executed
end
it "should handle ins commands" do
command = [["ins", "/Jar/Jar"]]
it "should handle ins commands with before" do
command = [["ins", "Binks", "after /Jar/Jar"]]
context = "/foo"
@resource.expects(:[]).times(2).returns(command).then.returns(context)
@augeas.expects(:insert).with("/foo/Jar/Jar")
@augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", false)
@augeas.expects(:save).returns(true)
@provider.execute_changes.should == :executed
end
it "should handle ins with no context" do
command = [["ins", "Binks", "after /Jar/Jar"]]
context = "" # this is the default
@resource.expects(:[]).times(2).returns(command).then.returns(context)
@augeas.expects(:insert).with("/Jar/Jar", "Binks", false)
@augeas.expects(:save).returns(true)
@provider.execute_changes.should == :executed
end
it "should handle multiple commands" do
command = [["ins", "/Jar/Jar"], ["clear", "/Jar/Jar"]]
command = [["ins", "Binks", "after /Jar/Jar"], ["clear", "/Jar/Jar"]]
context = "/foo"
@resource.expects(:[]).times(2).returns(command).then.returns(context)
@augeas.expects(:insert).with("/foo/Jar/Jar")
@augeas.expects(:clear).with("/foo/Jar/Jar")
@augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", false)
@augeas.expects(:clear).with("/foo/Jar/Jar")
@augeas.expects(:save).returns(true)
@provider.execute_changes.should == :executed
end

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

@ -47,4 +47,40 @@ describe Puppet::Provider::ParsedFile do
@class.instances
end
end
describe "when flushing a file's records to disk" do
before do
# This way we start with some @records, like we would in real life.
@class.stubs(:retrieve).returns []
@class.default_target = "/foo/bar"
@class.initvars
@class.prefetch
@filetype = mock 'filetype'
Puppet::Util::FileType.filetype(:flat).expects(:new).with("/my/file").returns @filetype
@filetype.stubs(:write)
end
it "should back up the file being written" do
@filetype.expects(:backup)
@class.flush_target("/my/file")
end
it "should not back up the file more than once between calls to 'prefetch'" do
@filetype.expects(:backup).once
@class.flush_target("/my/file")
@class.flush_target("/my/file")
end
it "should back the file up again once the file has been reread" do
@filetype.expects(:backup).times(2)
@class.flush_target("/my/file")
@class.prefetch
@class.flush_target("/my/file")
end
end
end

39
spec/unit/util/autoload.rb Executable file
Просмотреть файл

@ -0,0 +1,39 @@
#!/usr/bin/env ruby
Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
require 'puppet/util/autoload'
describe Puppet::Util::Autoload do
before do
@autoload = Puppet::Util::Autoload.new("foo", "tmp")
@autoload.stubs(:eachdir).yields "/my/dir"
end
describe "when loading a file" do
[RuntimeError, LoadError, SyntaxError].each do |error|
it "should not die an if a #{error.to_s} exception is thrown" do
FileTest.stubs(:exists?).returns true
Kernel.expects(:load).raises error
lambda { @autoload.load("foo") }.should_not raise_error
end
end
end
describe "when loading all files" do
before do
Dir.stubs(:glob).returns "file.rb"
end
[RuntimeError, LoadError, SyntaxError].each do |error|
it "should not die an if a #{error.to_s} exception is thrown" do
Kernel.expects(:require).raises error
lambda { @autoload.loadall }.should_not raise_error
end
end
end
end

110
spec/unit/util/filetype.rb Normal file
Просмотреть файл

@ -0,0 +1,110 @@
#!/usr/bin/env ruby
Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
require 'puppet/util/filetype'
# XXX Import all of the tests into this file.
describe Puppet::Util::FileType do
describe "when backing up a file" do
before do
@file = Puppet::Util::FileType.filetype(:flat).new("/my/file")
end
it "should do nothing if the file does not exist" do
File.expects(:exists?).with("/my/file").returns false
@file.expects(:bucket).never
@file.backup
end
it "should use its filebucket to backup the file if it exists" do
File.expects(:exists?).with("/my/file").returns true
bucket = mock 'bucket'
bucket.expects(:backup).with("/my/file")
@file.expects(:bucket).returns bucket
@file.backup
end
it "should use the filebucket named 'puppet' if it finds one" do
bucket = mock 'bucket'
bucket.expects(:bucket).returns "mybucket"
Puppet::Type.type(:filebucket).expects(:[]).with("puppet").returns bucket
@file.bucket.should == "mybucket"
end
it "should use the default filebucket if none named 'puppet' is found" do
bucket = mock 'bucket'
bucket.expects(:bucket).returns "mybucket"
Puppet::Type.type(:filebucket).expects(:[]).with("puppet").returns nil
Puppet::Type.type(:filebucket).expects(:mkdefaultbucket).returns bucket
@file.bucket.should == "mybucket"
end
end
describe "the flat filetype" do
before do
@type = Puppet::Util::FileType.filetype(:flat)
end
it "should exist" do
@type.should_not be_nil
end
describe "when the file already exists" do
it "should return the file's contents when asked to read it" do
file = @type.new("/my/file")
File.expects(:exist?).with("/my/file").returns true
File.expects(:read).with("/my/file").returns "my text"
file.read.should == "my text"
end
it "should unlink the file when asked to remove it" do
file = @type.new("/my/file")
File.expects(:exist?).with("/my/file").returns true
File.expects(:unlink).with("/my/file")
file.remove
end
end
describe "when the file does not exist" do
it "should return an empty string when asked to read the file" do
file = @type.new("/my/file")
File.expects(:exist?).with("/my/file").returns false
file.read.should == ""
end
end
describe "when writing the file" do
before do
@file = @type.new("/my/file")
FileUtils.stubs(:cp)
@tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file"
Tempfile.stubs(:new).returns @tempfile
end
it "should first create a temp file and copy its contents over to the file location" do
Tempfile.expects(:new).with("puppet").returns @tempfile
@tempfile.expects(:print).with("my text")
@tempfile.expects(:flush)
@tempfile.expects(:close)
FileUtils.expects(:cp).with(@tempfile.path, "/my/file")
@file.write "my text"
end
it "should set the selinux default context on the file" do
@file.expects(:set_selinux_default_context).with("/my/file")
@file.write "eh"
end
end
end
end

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

@ -1,137 +0,0 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../lib/puppettest'
require 'puppettest'
require 'puppet/util/filetype'
require 'mocha'
class TestFileType < Test::Unit::TestCase
include PuppetTest
def test_flat
obj = nil
path = tempfile()
type = nil
assert_nothing_raised {
type = Puppet::Util::FileType.filetype(:flat)
}
assert(type, "Could not retrieve flat filetype")
assert_nothing_raised {
obj = type.new(path)
}
text = "This is some text\n"
newtext = nil
assert_nothing_raised {
newtext = obj.read
}
# The base class doesn't allow a return of nil
assert_equal("", newtext, "Somehow got some text")
assert_nothing_raised {
obj.write(text)
}
assert_nothing_raised {
newtext = obj.read
}
assert_equal(text, newtext, "Text was changed somehow")
File.open(path, "w") { |f| f.puts "someyayness" }
text = File.read(path)
assert_nothing_raised {
newtext = obj.read
}
assert_equal(text, newtext, "Text was changed somehow")
end
# Make sure that modified files are backed up before they're changed.
def test_backup_is_called
path = tempfile
File.open(path, "w") { |f| f.print 'yay' }
obj = Puppet::Util::FileType.filetype(:flat).new(path)
obj.expects(:backup)
obj.write("something")
assert_equal("something", File.read(path), "File did not get changed")
end
def test_backup
path = tempfile
type = Puppet::Type.type(:filebucket)
obj = Puppet::Util::FileType.filetype(:flat).new(path)
# First try it when the file does not yet exist.
assert_nothing_raised("Could not call backup when file does not exist") do
obj.backup
end
# Then create the file
File.open(path, "w") { |f| f.print 'one' }
# Then try it with no filebucket objects
assert_nothing_raised("Could not call backup with no buckets") do
obj.backup
end
puppet = type.mkdefaultbucket
assert(puppet, "Did not create default filebucket")
assert_equal("one", puppet.bucket.getfile(Digest::MD5.hexdigest(File.read(path))), "Could not get file from backup")
# Try it again when the default already exists
File.open(path, "w") { |f| f.print 'two' }
assert_nothing_raised("Could not call backup with no buckets") do
obj.backup
end
assert_equal("two", puppet.bucket.getfile(Digest::MD5.hexdigest(File.read(path))), "Could not get file from backup")
end
if Facter["operatingsystem"].value == "Darwin" and Facter["operatingsystemrelease"] != "9.1.0"
def test_ninfotoarray
obj = nil
type = nil
assert_nothing_raised {
type = Puppet::Util::FileType.filetype(:netinfo)
}
assert(type, "Could not retrieve netinfo filetype")
%w{users groups aliases}.each do |map|
assert_nothing_raised {
obj = type.new(map)
}
assert_nothing_raised("could not read map %s" % map) {
obj.read
}
array = nil
assert_nothing_raised("Failed to parse %s map" % map) {
array = obj.to_array
}
assert_instance_of(Array, array)
array.each do |record|
assert_instance_of(Hash, record)
assert(record.length != 0)
end
end
end
end
end