Merge branch '2.7.x'

* 2.7.x:
  Fix communication_error acceptance test
  Use :as_platform in the smf service spec
  Use :as_platform in the redhat service spec
  (#13690) add comments to spec_helper
  (#12392) Don't require eventlog gem on non-Windows platforms
  Updating CHANGELOG and lib/puppet.rb for 2.7.13
  (#13690) Update 2.7.x for spec_helper compatibility with external projects
  (#12392) Add eventlog destination and log to it by default on Windows
  Stub mktmpdir and remove_entry_secure in os x package providers
  (#13260) Spec test to verify that mktmpdir is used
  (#12392) Created Windows eventlog message resource dll
  (#13260) Use mktmpdir when downloading packages
  Refactor pkgdmg specs
  Remove telnet Output_log parameter
  Fix for bucket_path security vulnerability
  Removed text/marshal support
  (#12466) unset X-Forwarded-For header
  (#13204) Workaround duplicate Augeas save events

Conflicts:
	lib/puppet/util/settings.rb
	spec/spec_helper.rb
This commit is contained in:
Patrick Carlisle 2012-04-11 17:34:20 -07:00
commit daee52047b
31 changed files with 594 additions and 292 deletions

View File

@ -1,3 +1,13 @@
2.7.13
===
1f58ea6 Stub mktmpdir and remove_entry_secure in os x package providers
b7553a5 (#13260) Spec test to verify that mktmpdir is used
46e8dc0 (#13260) Use mktmpdir when downloading packages
b36bda9 Refactor pkgdmg specs
91e7ce4 Remove telnet Output_log parameter
0d6d299 Fix for bucket_path security vulnerability
19bd30a Removed text/marshal support
2.7.12
===
36ca299 Update packaging spec files for 2.7.12

View File

@ -5,10 +5,10 @@ apply_manifest_on master, "host { 'forge.puppetlabs.com': ip => '127.0.0.2' }"
step "Search against a non-existent Forge"
on master, puppet("module search yup"), :acceptable_exit_codes => [1] do
assert_equal <<-STDOUT, stdout
assert_match <<-STDOUT, stdout
Searching http://forge.puppetlabs.com ...
STDOUT
assert_equal <<-STDERR, stderr
assert_match <<-STDERR, stderr
Error: Could not connect to http://forge.puppetlabs.com
There was a network communications problem
Check your network connection and try again

View File

@ -0,0 +1,26 @@
test_name "Write to Windows eventlog"
confine :to, :platform => 'windows'
def get_cmd(host)
if options[:type] =~ /pe/
"#{host['puppetbindir']}/ruby"
else
'ruby'
end
end
agents.each do |agent|
# get remote time
now = on(agent, "#{get_cmd(agent)} -e \"puts Time.now.utc.strftime('%m/%d/%Y %H:%M:%S')\"").stdout.chomp
# generate an error, no master on windows boxes
on agent, puppet_agent('--server', '127.0.0.1', '--test'), :acceptable_exit_codes => [1]
# make sure there's a Puppet error message in the log
# cygwin + ssh + wmic hangs trying to read stdin, so echo '' |
on agent, "cmd /c echo '' | wmic ntevent where \"LogFile='Application' and SourceName='Puppet' and TimeWritten >= '#{now}'\" get Message,Type /format:csv" do
fail_test "Event not found in Application event log" unless
stdout =~ /Could not retrieve catalog from remote server.*,Error/m
end
end

32
conf/windows/eventlog/Rakefile Executable file
View File

@ -0,0 +1,32 @@
require 'rubygems'
require 'rake'
require 'fileutils'
require 'rbconfig'
BASENAME = "puppetres"
task :default do
sh 'rake -T'
end
desc 'Build puppet eventlog message dll'
task :dist => ['out', "#{BASENAME}.dll"]
directory 'out'
rule '.rc' => '.mc' do |t|
sh "mc -b -r out -h out #{t.source}"
end
rule '.res' => '.rc' do |t|
sh "rc -nologo -r -fo out/#{t.name} out/#{t.source}"
end
rule '.dll' => '.res' do |t|
sh "link -nologo -dll -noentry -machine:x86 -out:out/#{t.name} out/#{t.source}"
end
desc 'Delete generated files'
task :clean do
FileUtils.rm_rf('out')
end

Binary file not shown.

View File

@ -0,0 +1,18 @@
MessageId=0x1
SymbolicName=PUPPET_INFO
Language=English
%1
.
MessageId=0x2
SymbolicName=PUPPET_WARN
Language=English
%1
.
MessageId=0x3
SymbolicName=PUPPET_ERROR
Language=English
%1
.

View File

@ -26,6 +26,9 @@ Listen 8140
SSLVerifyDepth 1
SSLOptions +StdEnvVars
# This header needs to be set if using a loadbalancer or proxy
RequestHeader unset X-Forwarded-For
RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e

View File

@ -86,7 +86,29 @@ def do_configs(configs, target, strip = 'conf/')
else
FileUtils.install(cf, ocf, {:mode => 0644, :verbose => true})
end
end
end
if $operatingsystem == 'windows'
src_dll = 'conf/windows/eventlog/puppetres.dll'
dst_dll = File.join(InstallOptions.bin_dir, 'puppetres.dll')
if $haveftools
File.install(src_dll, dst_dll, 0644, true)
else
FileUtils.install(src_dll, dst_dll, {:mode => 0644, :verbose => true})
end
require 'win32/registry'
include Win32::Registry::Constants
begin
Win32::Registry::HKEY_LOCAL_MACHINE.create('SYSTEM\CurrentControlSet\services\eventlog\Application\Puppet', KEY_ALL_ACCESS | 0x0100) do |reg|
reg.write_s('EventMessageFile', dst_dll.tr('/', '\\'))
reg.write_i('TypesSupported', 0x7)
end
rescue Win32::Registry::Error => e
warn "Failed to create puppet eventlog registry key: #{e}"
end
end
end
def do_bins(bins, target, strip = 's?bin/')

View File

@ -24,7 +24,7 @@ require 'puppet/util/run_mode'
# it's also a place to find top-level commands like 'debug'
module Puppet
PUPPETVERSION = '2.7.12'
PUPPETVERSION = '2.7.13'
def Puppet.version
PUPPETVERSION

View File

@ -0,0 +1,6 @@
require 'puppet/util/feature'
if Puppet.features.microsoft_windows?
Puppet.features.rubygems?
Puppet.features.add(:eventlog, :libs => %{win32/eventlog})
end

View File

@ -77,33 +77,6 @@ Puppet::Network::FormatHandler.create_serialized_formats(:b64_zlib_yaml) do
end
end
Puppet::Network::FormatHandler.create(:marshal, :mime => "text/marshal") do
# Marshal doesn't need the class name; it's serialized.
def intern(klass, text)
Marshal.load(text)
end
# Marshal doesn't need the class name; it's serialized.
def intern_multiple(klass, text)
Marshal.load(text)
end
def render(instance)
Marshal.dump(instance)
end
# Marshal monkey-patches Array, so this works.
def render_multiple(instances)
Marshal.dump(instances)
end
# Everything's supported
def supported?(klass)
true
end
end
Puppet::Network::FormatHandler.create(:s, :mime => "text/plain", :extension => "txt")
# A very low-weight format so it'll never get chosen automatically.

View File

@ -31,6 +31,7 @@ module Puppet::Network::HTTP::API::V1
method = indirection_method(http_method, indirection)
params[:environment] = Puppet::Node::Environment.new(environment)
params.delete(:bucket_path)
raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil?

View File

@ -334,6 +334,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
root = resource[:root].sub(/^\/$/, "")
saved_files.each do |key|
saved_file = @aug.get(key).sub(/^\/files/, root)
next unless File.exists?(saved_file + ".augnew")
if Puppet[:show_diff]
notice "\n" + diff(saved_file, saved_file + ".augnew")
end

View File

@ -50,23 +50,24 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag
def self.installpkgdmg(source, name)
unless source =~ /\.dmg$/i
self.fail "Mac OS X PKG DMG's must specificy a source string ending in .dmg"
self.fail "Mac OS X PKG DMG's must specify a source string ending in .dmg"
end
require 'open-uri'
require 'facter/util/plist'
cached_source = source
if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source
cached_source = "/tmp/#{name}"
begin
curl "-o", cached_source, "-C", "-", "-k", "-L", "-s", "--url", source
Puppet.debug "Success: curl transfered [#{name}]"
rescue Puppet::ExecutionFailure
Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods."
cached_source = source
end
end
tmpdir = Dir.mktmpdir
begin
if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source
cached_source = File.join(tmpdir, name)
begin
curl "-o", cached_source, "-C", "-", "-k", "-L", "-s", "--url", source
Puppet.debug "Success: curl transfered [#{name}]"
rescue Puppet::ExecutionFailure
Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods."
cached_source = source
end
end
open(cached_source) do |dmg|
xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-mountrandom", "/tmp", dmg.path
ptable = Plist::parse_xml xml_str
@ -87,8 +88,7 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag
end
end
ensure
# JJM Remove the file if open-uri didn't already do so.
File.unlink(cached_source) if File.exist?(cached_source)
FileUtils.remove_entry_secure(tmpdir, force=true)
end
end

View File

@ -39,11 +39,7 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag
def self.instances
instance_by_name.collect do |name|
new(
:name => name,
:provider => :pkgdmg,
:ensure => :installed
)
new(:name => name, :provider => :pkgdmg, :ensure => :installed)
end
end
@ -58,22 +54,23 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag
def self.installpkgdmg(source, name)
unless source =~ /\.dmg$/i || source =~ /\.pkg$/i
raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file")
raise Puppet::Error.new("Mac OS X PKG DMG's must specify a source string ending in .dmg or flat .pkg file")
end
require 'open-uri'
cached_source = source
if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source
cached_source = "/tmp/#{name}"
begin
curl "-o", cached_source, "-C", "-", "-k", "-L", "-s", "--url", source
Puppet.debug "Success: curl transfered [#{name}]"
rescue Puppet::ExecutionFailure
Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods."
cached_source = source
end
end
tmpdir = Dir.mktmpdir
begin
if %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ cached_source
cached_source = File.join(tmpdir, name)
begin
curl "-o", cached_source, "-C", "-", "-k", "-L", "-s", "--url", source
Puppet.debug "Success: curl transfered [#{name}]"
rescue Puppet::ExecutionFailure
Puppet.debug "curl did not transfer [#{name}]. Falling back to slower open-uri transfer methods."
cached_source = source
end
end
if source =~ /\.dmg$/i
File.open(cached_source) do |dmg|
xml_str = hdiutil "mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", dmg.path
@ -96,14 +93,11 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag
end
end
end
elsif source =~ /\.pkg$/i
installpkg(cached_source, name, source)
else
raise Puppet::Error.new("Mac OS X PKG DMG's must specificy a source string ending in .dmg or flat .pkg file")
installpkg(cached_source, name, source)
end
ensure
# JJM Remove the file if open-uri didn't already do so.
File.unlink(cached_source) if File.exist?(cached_source)
FileUtils.remove_entry_secure(tmpdir, force=true)
end
end

View File

@ -0,0 +1,170 @@
module Puppet::Test
# This class is intended to provide an API to be used by external projects
# when they are running tests that depend on puppet core. This should
# allow us to vary the implementation details of managing puppet's state
# for testing, from one version of puppet to the next--without forcing
# the external projects to do any of that state management or be aware of
# the implementation details.
#
# For now, this consists of a few very simple signatures. The plan is
# that it should be the responsibility of the puppetlabs_spec_helper
# to broker between external projects and this API; thus, if any
# hacks are required (e.g. to determine whether or not a particular)
# version of puppet supports this API, those hacks will be consolidated in
# one place and won't need to be duplicated in every external project.
#
# This should also alleviate the anti-pattern that we've been following,
# wherein each external project starts off with a copy of puppet core's
# test_helper.rb and is exposed to risk of that code getting out of
# sync with core.
#
# Since this class will be "library code" that ships with puppet, it does
# not use API from any existing test framework such as rspec. This should
# theoretically allow it to be used with other unit test frameworks in the
# future, if desired.
#
# Note that in the future this API could potentially be expanded to handle
# other features such as "around_test", but we didn't see a compelling
# reason to deal with that right now.
class TestHelper
# Call this method once, when beginning a test run--prior to running
# any individual tests.
# @return nil
def self.before_all_tests()
end
# Call this method once, at the end of a test run, when no more tests
# will be run.
# @return nil
def self.after_all_tests()
end
# Call this method once per test, prior to execution of each invididual test.
# @return nil
def self.before_each_test()
# We need to preserve the current state of all our indirection cache and
# terminus classes. This is pretty important, because changes to these
# are global and lead to order dependencies in our testing.
#
# We go direct to the implementation because there is no safe, sane public
# API to manage restoration of these to their default values. This
# should, once the value is proved, be moved to a standard API on the
# indirector.
#
# To make things worse, a number of the tests stub parts of the
# indirector. These stubs have very specific expectations that what
# little of the public API we could use is, well, likely to explode
# randomly in some tests. So, direct access. --daniel 2011-08-30
$saved_indirection_state = {}
indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)
indirections.each do |indirector|
$saved_indirection_state[indirector.name] = {
:@terminus_class => indirector.instance_variable_get(:@terminus_class),
:@cache_class => indirector.instance_variable_get(:@cache_class)
}
end
# The process environment is a shared, persistent resource.
$old_env = ENV.to_hash
# So is the load_path
$old_load_path = $LOAD_PATH.dup
initialize_settings_before_each()
end
# Call this method once per test, after execution of each individual test.
# @return nil
def self.after_each_test()
Puppet.settings.send(:clear_everything_for_tests)
Puppet::Node::Environment.clear
Puppet::Util::Storage.clear
Puppet::Util::ExecutionStub.reset
Puppet.clear_deprecation_warnings
# uncommenting and manipulating this can be useful when tracking down calls to deprecated code
#Puppet.log_deprecations_to_file("deprecations.txt", /^Puppet::Util.exec/)
# Restore the indirector configuration. See before hook.
indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)
indirections.each do |indirector|
$saved_indirection_state.fetch(indirector.name, {}).each do |variable, value|
indirector.instance_variable_set(variable, value)
end
end
$saved_indirection_state = nil
# Restore the global process environment. Can't just assign because this
# is a magic variable, sadly, and doesn't do that™. It is sufficiently
# faster to use the compare-then-set model to avoid excessive work that it
# justifies the complexity. --daniel 2012-03-15
unless ENV.to_hash == $old_env
ENV.clear
$old_env.each {|k, v| ENV[k] = v }
end
# Some tests can cause us to connect, in which case the lingering
# connection is a resource that can cause unexpected failure in later
# tests, as well as sharing state accidentally.
# We're testing if ActiveRecord::Base is defined because some test cases
# may stub Puppet.features.rails? which is how we should normally
# introspect for this functionality.
ActiveRecord::Base.remove_connection if defined?(ActiveRecord::Base)
# Restore the load_path late, to avoid messing with stubs from the test.
$LOAD_PATH.clear
$old_load_path.each {|x| $LOAD_PATH << x }
end
#########################################################################################
# PRIVATE METHODS (not part of the public TestHelper API--do not call these from outside
# of this class!)
#########################################################################################
def self.app_defaults_for_tests()
{
:run_mode => :user,
:name => :apply,
:logdir => "/dev/null",
:confdir => "/dev/null",
:vardir => "/dev/null",
:rundir => "/dev/null",
}
end
private_class_method :app_defaults_for_tests
def self.initialize_settings_before_each()
# Initialize "app defaults" settings to a good set of test values
app_defaults_for_tests.each do |key, value|
Puppet.settings.set_value(key, value, :application_defaults)
end
# Avoid opening ports to the outside world
Puppet.settings[:bindaddress] = "127.0.0.1"
# We don't want to depend upon the reported domain name of the
# machine running the tests, nor upon the DNS setup of that
# domain.
Puppet.settings[:use_srv_records] = false
# Longer keys are secure, but they sure make for some slow testing - both
# in terms of generating keys, and in terms of anything the next step down
# the line doing validation or whatever. Most tests don't care how long
# or secure it is, just that it exists, so these are better and faster
# defaults, in testing only.
#
# I would make these even shorter, but OpenSSL doesn't support anything
# below 512 bits. Sad, really, because a 0 bit key would be just fine.
Puppet[:req_bits] = 512
Puppet[:keylength] = 512
end
private_class_method :initialize_settings_before_each
end
end

View File

@ -196,7 +196,9 @@ class Puppet::Util::Log
end
def self.setup_default
Log.newdestination(Puppet.features.syslog? ? :syslog : Puppet[:puppetdlog])
Log.newdestination(
(Puppet.features.syslog? ? :syslog :
(Puppet.features.eventlog? ? :eventlog : Puppet[:puppetdlog])))
end
# Is the passed level a valid log level?

View File

@ -231,3 +231,41 @@ Puppet::Util::Log.newdesttype :array do
end
end
Puppet::Util::Log.newdesttype :eventlog do
def self.suitable?(obj)
Puppet.features.eventlog?
end
def initialize
@eventlog = Win32::EventLog.open("Application")
end
def to_native(level)
case level
when :debug,:info,:notice
[Win32::EventLog::INFO, 0x01]
when :warning
[Win32::EventLog::WARN, 0x02]
when :err,:alert,:emerg,:crit
[Win32::EventLog::ERROR, 0x03]
end
end
def handle(msg)
native_type, native_id = to_native(msg.level)
@eventlog.report_event(
:source => "Puppet",
:event_type => native_type,
:event_id => native_id,
:data => (msg.source and msg.source != 'Puppet' ? "#{msg.source}: " : '') + msg.to_s
)
end
def close
if @eventlog
@eventlog.close
@eventlog = nil
end
end
end

View File

@ -15,7 +15,7 @@ class Puppet::Util::NetworkDevice::Transport::Telnet < Puppet::Util::NetworkDevi
def connect
@telnet = Net::Telnet::new("Host" => host, "Port" => port || 23,
"Timeout" => 10,
"Prompt" => default_prompt, "Output_log" => "/tmp/out.log")
"Prompt" => default_prompt)
end
def close
@ -39,4 +39,4 @@ class Puppet::Util::NetworkDevice::Transport::Telnet < Puppet::Util::NetworkDevi
def send(line)
@telnet.puts(line)
end
end
end

View File

@ -1131,46 +1131,6 @@ if @config.include?(:run_mode)
end
end
# Private method for internal test use only; allows to assign reasonable values to the "app defaults"
# settings for the purpose of test runs.
#
# @return [Hash] a hash containing the default values to use for application settings during testing.
def app_defaults_for_tests()
{
:run_mode => :user,
:name => :apply,
:logdir => "/dev/null",
:confdir => "/dev/null",
:vardir => "/dev/null",
:rundir => "/dev/null",
}
end
private :app_defaults_for_tests
# Private method for internal test use only; allows to assign reasonable values to the "app defaults"
# settings for the purpose of test runs.
#
# @return nil
def initialize_everything_for_tests()
# Initialize "app defaults" settings to a good set of test values
app_defaults_for_tests.each do |key, value|
Puppet.settings.set_value(key, value, :application_defaults)
end
# Avoid opening ports to the outside world
Puppet.settings[:bindaddress] = "127.0.0.1"
# We don't want to depend upon the reported domain name of the
# machine running the tests, nor upon the DNS setup of that
# domain.
Puppet.settings[:use_srv_records] = false
end
private :initialize_everything_for_tests
# Private method for internal test use only; allows to do a comprehensive clear of all settings between tests.
#
# @return nil
@ -1182,5 +1142,4 @@ if @config.include?(:run_mode)
end
private :clear_everything_for_tests
end

View File

@ -1,3 +1,7 @@
# NOTE: a lot of the stuff in this file is duplicated in the "puppet_spec_helper" in the project
# puppetlabs_spec_helper. We should probably eat our own dog food and get rid of most of this from here,
# and have the puppet core itself use puppetlabs_spec_helper
dir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift File.join(dir, 'lib')
@ -25,6 +29,7 @@ require 'puppet_spec/matchers'
require 'puppet_spec/database'
require 'monkey_patches/alias_should_to_must'
require 'monkey_patches/publicize_methods'
require 'puppet/test/test_helper'
Pathname.glob("#{dir}/shared_contexts/*.rb") do |file|
require file.relative_path_from(Pathname.new(dir))
@ -39,113 +44,55 @@ RSpec.configure do |config|
config.mock_with :mocha
config.before :all do
Puppet::Test::TestHelper.before_all_tests()
end
config.after :all do
Puppet::Test::TestHelper.after_all_tests()
end
config.before :each do
# Disabling garbage collection inside each test, and only running it at
# the end of each block, gives us an ~ 15 percent speedup, and more on
# some platforms *cough* windows *cough* that are a little slower.
GC.disable
# We need to preserve the current state of all our indirection cache and
# terminus classes. This is pretty important, because changes to these
# are global and lead to order dependencies in our testing.
#
# We go direct to the implementation because there is no safe, sane public
# API to manage restoration of these to their default values. This
# should, once the value is proved, be moved to a standard API on the
# indirector.
#
# To make things worse, a number of the tests stub parts of the
# indirector. These stubs have very specific expectations that what
# little of the public API we could use is, well, likely to explode
# randomly in some tests. So, direct access. --daniel 2011-08-30
$saved_indirection_state = {}
indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)
indirections.each do |indirector|
$saved_indirection_state[indirector.name] = {
:@terminus_class => indirector.instance_variable_get(:@terminus_class),
:@cache_class => indirector.instance_variable_get(:@cache_class)
}
end
# The process environment is a shared, persistent resource.
$old_env = ENV.to_hash
# So is the load_path
$old_load_path = $LOAD_PATH.dup
# REVISIT: I think this conceals other bad tests, but I don't have time to
# fully diagnose those right now. When you read this, please come tell me
# I suck for letting this float. --daniel 2011-04-21
Signal.stubs(:trap)
Puppet.settings.send(:initialize_everything_for_tests)
# Longer keys are secure, but they sure make for some slow testing - both
# in terms of generating keys, and in terms of anything the next step down
# the line doing validation or whatever. Most tests don't care how long
# or secure it is, just that it exists, so these are better and faster
# defaults, in testing only.
# TODO: in a saner world, we'd move this logging redirection into our TestHelper class.
# Without doing so, external projects will all have to roll their own solution for
# redirecting logging, and for validating expected log messages. However, because the
# current implementation of this involves creating an instance variable "@logs" on
# EVERY SINGLE TEST CLASS, and because there are over 1300 tests that are written to expect
# this instance variable to be available--we can't easily solve this problem right now.
#
# I would make these even shorter, but OpenSSL doesn't support anything
# below 512 bits. Sad, really, because a 0 bit key would be just fine.
Puppet[:req_bits] = 512
Puppet[:keylength] = 512
# redirecting logging away from console, because otherwise the test output will be
# obscured by all of the log output
@logs = []
Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
@log_level = Puppet::Util::Log.level
Puppet::Test::TestHelper.before_each_test()
end
config.after :each do
Puppet.settings.send(:clear_everything_for_tests)
Puppet::Node::Environment.clear
Puppet::Util::Storage.clear
Puppet::Util::ExecutionStub.reset
PuppetSpec::Files.cleanup
Puppet::Test::TestHelper.after_each_test()
# TODO: this should be abstracted in the future--see comments above the '@logs' block in the
# "before" code above.
#
# clean up after the logging changes that we made before each test.
@logs.clear
Puppet::Util::Log.close_all
Puppet::Util::Log.level = @log_level
Puppet.clear_deprecation_warnings
# uncommenting and manipulating this can be useful when tracking down calls to deprecated code
#Puppet.log_deprecations_to_file("deprecations.txt", /^Puppet::Util.exec/)
# Restore the indirector configuration. See before hook.
indirections = Puppet::Indirector::Indirection.send(:class_variable_get, :@@indirections)
indirections.each do |indirector|
$saved_indirection_state.fetch(indirector.name, {}).each do |variable, value|
indirector.instance_variable_set(variable, value)
end
end
$saved_indirection_state = {}
# Restore the global process environment. Can't just assign because this
# is a magic variable, sadly, and doesn't do that™. It is sufficiently
# faster to use the compare-then-set model to avoid excessive work that it
# justifies the complexity. --daniel 2012-03-15
unless ENV.to_hash == $old_env
ENV.clear
$old_env.each {|k, v| ENV[k] = v }
end
# Some tests can cause us to connect, in which case the lingering
# connection is a resource that can cause unexpected failure in later
# tests, as well as sharing state accidentally.
# We're testing if ActiveRecord::Base is defined because some test cases
# may stub Puppet.features.rails? which is how we should normally
# introspect for this functionality.
ActiveRecord::Base.remove_connection if defined?(ActiveRecord::Base)
# Restore the load_path late, to avoid messing with stubs from the test.
$LOAD_PATH.clear
$old_load_path.each {|x| $LOAD_PATH << x }
# This will perform a GC between tests, but only if actually required. We
# experimented with forcing a GC run, and that was less efficient than
# just letting it run all the time.

View File

@ -162,49 +162,6 @@ describe "Puppet Network Format" do
end
it "should include a marshal format" do
Puppet::Network::FormatHandler.format(:marshal).should_not be_nil
end
describe "marshal" do
before do
@marshal = Puppet::Network::FormatHandler.format(:marshal)
end
it "should have its mime type set to text/marshal" do
Puppet::Network::FormatHandler.format(:marshal).mime.should == "text/marshal"
end
it "should be supported on Strings" do
@marshal.should be_supported(String)
end
it "should render by calling 'Marshal.dump' on the instance" do
instance = mock 'instance'
Marshal.expects(:dump).with(instance).returns "foo"
@marshal.render(instance).should == "foo"
end
it "should render multiple instances by calling 'to_marshal' on the array" do
instances = [mock('instance')]
Marshal.expects(:dump).with(instances).returns "foo"
@marshal.render_multiple(instances).should == "foo"
end
it "should intern by calling 'Marshal.load'" do
text = "foo"
Marshal.expects(:load).with("foo").returns "bar"
@marshal.intern(String, text).should == "bar"
end
it "should intern multiples by calling 'Marshal.load'" do
text = "foo"
Marshal.expects(:load).with("foo").returns "bar"
@marshal.intern_multiple(String, text).should == "bar"
end
end
describe "plaintext" do
before do
@text = Puppet::Network::FormatHandler.format(:s)

View File

@ -42,6 +42,14 @@ describe Puppet::Network::HTTP::API::V1 do
@tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].to_s.should == "env"
end
it "should not pass a buck_path parameter through (See Bugs #13553, #13518, #13511)" do
@tester.uri2indirection("GET", "/env/foo/bar", { :bucket_path => "/malicious/path" })[3].should_not include({ :bucket_path => "/malicious/path" })
end
it "should pass allowed parameters through" do
@tester.uri2indirection("GET", "/env/foo/bar", { :allowed_param => "value" })[3].should include({ :allowed_param => "value" })
end
it "should return the environment as a Puppet::Node::Environment" do
@tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should be_a Puppet::Node::Environment
end

View File

@ -330,6 +330,7 @@ describe provider_class do
it "should call diff when a file is shown to have been changed" do
file = "/etc/hosts"
File.stubs(:delete)
File.stubs(:exists?).returns(true)
@resource[:context] = "/files"
@resource[:changes] = ["set #{file}/foo bar"]
@ -347,6 +348,7 @@ describe provider_class do
file1 = "/etc/hosts"
file2 = "/etc/resolv.conf"
File.stubs(:delete)
File.stubs(:exists?).returns(true)
@resource[:context] = "/files"
@resource[:changes] = ["set #{file1}/foo bar", "set #{file2}/baz biz"]
@ -367,6 +369,7 @@ describe provider_class do
root = "/tmp/foo"
file = "/etc/hosts"
File.stubs(:delete)
File.stubs(:exists?).returns(true)
@resource[:context] = "/files"
@resource[:changes] = ["set #{file}/foo bar"]
@ -399,6 +402,7 @@ describe provider_class do
it "should cleanup the .augnew file" do
file = "/etc/hosts"
File.stubs(:exists?).returns(true)
@resource[:context] = "/files"
@resource[:changes] = ["set #{file}/foo bar"]
@ -414,6 +418,30 @@ describe provider_class do
@provider.should be_need_to_run
end
# Workaround for Augeas bug #264 which reports filenames twice
it "should handle duplicate /augeas/events/saved filenames" do
file = "/etc/hosts"
augnew = states("augnew").starts_as("present")
File.stubs(:exists?).returns(true).when(augnew.is("present"))
File.stubs(:exists?).returns(false).when(augnew.is("absent"))
@resource[:context] = "/files"
@resource[:changes] = ["set #{file}/foo bar"]
@augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved[1]", "/augeas/events/saved[2]"])
@augeas.stubs(:get).with("/augeas/events/saved[1]").returns("/files#{file}")
@augeas.stubs(:get).with("/augeas/events/saved[2]").returns("/files#{file}")
@augeas.expects(:set).with("/augeas/save", "newfile")
@augeas.expects(:close)
File.expects(:delete).with(file + ".augnew").when(augnew.is("present")).then(augnew.is("absent"))
@provider.expects(:diff).with("#{file}", "#{file}.augnew").returns("").when(augnew.is("present"))
@provider.should be_need_to_run
end
it "should fail with an error if saving fails" do
file = "/etc/hosts"

View File

@ -0,0 +1,42 @@
#!/usr/bin/env rspec
require 'spec_helper'
describe Puppet::Type.type(:package).provider(:appdmg) do
let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :appdmg) }
let(:provider) { described_class.new(resource) }
describe "when installing an appdmg" do
let(:fake_mountpoint) { "/tmp/dmg.foo" }
let(:empty_hdiutil_plist) { Plist::Emit.dump({}) }
let(:fake_hdiutil_plist) { Plist::Emit.dump({"system-entities" => [{"mount-point" => fake_mountpoint}]}) }
before do
fh = mock 'filehandle'
fh.stubs(:path).yields "/tmp/foo"
resource[:source] = "foo.dmg"
described_class.stubs(:open).yields fh
Dir.stubs(:mktmpdir).returns "/tmp/testtmp123"
FileUtils.stubs(:remove_entry_secure)
end
describe "from a remote source" do
let(:tmpdir) { "/tmp/good123" }
before :each do
resource[:source] = "http://fake.puppetlabs.com/foo.dmg"
end
it "should call tmpdir and use the returned directory" do
Dir.expects(:mktmpdir).returns tmpdir
Dir.stubs(:entries).returns ["foo.app"]
described_class.expects(:curl).with do |*args|
args[0] == "-o" and args[1].include? tmpdir
end
described_class.stubs(:hdiutil).returns fake_hdiutil_plist
described_class.expects(:installapp)
provider.install
end
end
end
end

View File

@ -1,83 +1,89 @@
#!/usr/bin/env rspec
require 'spec_helper'
provider = Puppet::Type.type(:package).provider(:pkgdmg)
describe Puppet::Type.type(:package).provider(:pkgdmg) do
let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :pkgdmg) }
let(:provider) { described_class.new(resource) }
describe provider do
before do
@resource = stub 'resource', :[] => "dummypkgdmg"
@provider = provider.new(@resource)
@fakemountpoint = "/tmp/dmg.foo"
@fakepkgfile = "/tmp/test.pkg"
@fakehdiutilinfo = {"system-entities" => [{"mount-point" => @fakemountpoint}] }
@fakehdiutilplist = Plist::Emit.dump(@fakehdiutilinfo)
@hdiutilmountargs = ["mount", "-plist", "-nobrowse", "-readonly",
"-noidme", "-mountrandom", "/tmp"]
end
it "should not be versionable" do
provider.versionable?.should be_false
end
it "should not be uninstallable" do
provider.uninstallable?.should be_false
end
it { should_not be_versionable }
it { should_not be_uninstallable }
describe "when installing it should fail when" do
it "no source is specified" do
@resource.stubs(:[]).with(:source).returns nil
lambda { @provider.install }.should raise_error(Puppet::Error)
before :each do
Puppet::Util.expects(:execute).never
end
it "no name is specified" do
@resource.stubs(:[]).with(:name).returns nil
lambda { @provider.install }.should raise_error(Puppet::Error)
it "no source is specified" do
expect { provider.install }.should raise_error(Puppet::Error, /must specify a package source/)
end
it "the source does not end in .dmg or .pkg" do
@resource.stubs(:[]).with(:source).returns "notendingindotdmgorpkg"
lambda { @provider.install }.should raise_error(Puppet::Error)
end
it "a disk image with no system entities is mounted" do
@provider.stubs(:[]).with(:hdiutil).returns ""
lambda { @provider.install }.should raise_error(Puppet::Error)
resource[:source] = "bar"
expect { provider.install }.should raise_error(Puppet::Error, /must specify a source string ending in .*dmg.*pkg/)
end
end
# These tests shouldn't be this messy. The pkgdmg provider needs work...
describe "when installing a pkgdmg" do
let(:fake_mountpoint) { "/tmp/dmg.foo" }
let(:empty_hdiutil_plist) { Plist::Emit.dump({}) }
let(:fake_hdiutil_plist) { Plist::Emit.dump({"system-entities" => [{"mount-point" => fake_mountpoint}]}) }
before do
fh = mock 'filehandle'
fh.stubs(:path).yields "/tmp/foo"
@resource.stubs(:[]).with(:source).returns "foo.dmg"
resource[:source] = "foo.dmg"
File.stubs(:open).yields fh
Dir.stubs(:mktmpdir).returns "/tmp/testtmp123"
FileUtils.stubs(:remove_entry_secure)
end
it "should fail when a disk image with no system entities is mounted" do
described_class.stubs(:hdiutil).returns(empty_hdiutil_plist)
expect { provider.install }.should raise_error(Puppet::Error, /No disk entities/)
end
it "should call hdiutil to mount and eject the disk image" do
Dir.stubs(:entries).returns []
@provider.class.expects(:hdiutil).with("eject", @fakemountpoint).returns 0
@provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns @fakehdiutilplist
@provider.install
provider.class.expects(:hdiutil).with("eject", fake_mountpoint).returns 0
provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns fake_hdiutil_plist
provider.install
end
it "should call installpkg if a pkg/mpkg is found on the dmg" do
Dir.stubs(:entries).returns ["foo.pkg"]
@provider.class.stubs(:hdiutil).returns @fakehdiutilplist
@provider.class.expects(:installpkg).with("#{@fakemountpoint}/foo.pkg", @resource[:name], "foo.dmg").returns ""
@provider.install
provider.class.stubs(:hdiutil).returns fake_hdiutil_plist
provider.class.expects(:installpkg).with("#{fake_mountpoint}/foo.pkg", resource[:name], "foo.dmg").returns ""
provider.install
end
describe "from a remote source" do
let(:tmpdir) { "/tmp/good123" }
before :each do
resource[:source] = "http://fake.puppetlabs.com/foo.dmg"
end
it "should call tmpdir and use the returned directory" do
Dir.expects(:mktmpdir).returns tmpdir
Dir.stubs(:entries).returns ["foo.pkg"]
described_class.expects(:curl).with do |*args|
args[0] == "-o" and args[1].include? tmpdir
end
described_class.stubs(:hdiutil).returns fake_hdiutil_plist
described_class.expects(:installpkg)
provider.install
end
end
end
describe "when installing flat pkg file" do
it "should call installpkg if a flat pkg file is found instead of a .dmg image" do
@resource.stubs(:[]).with(:source).returns "/tmp/test.pkg"
@resource.stubs(:[]).with(:name).returns "testpkg"
@provider.class.expects(:installpkgdmg).with("#{@fakepkgfile}", "testpkg").returns ""
@provider.install
end
resource[:source] = "/tmp/test.pkg"
resource[:name] = "testpkg"
provider.class.expects(:installpkgdmg).with("/tmp/test.pkg", "testpkg").returns ""
provider.install
end
end
end

View File

@ -6,11 +6,9 @@ require 'spec_helper'
provider_class = Puppet::Type.type(:service).provider(:redhat)
describe provider_class do
describe provider_class, :as_platform => :posix do
before :each do
Puppet.features.stubs(:posix?).returns(true)
Puppet.features.stubs(:microsoft_windows?).returns(false)
@class = Puppet::Type.type(:service).provider(:redhat)
@resource = stub 'resource'
@resource.stubs(:[]).returns(nil)

View File

@ -8,11 +8,9 @@ require 'spec_helper'
provider_class = Puppet::Type.type(:service).provider(:smf)
describe provider_class do
describe provider_class, :as_platform => :posix do
before(:each) do
Puppet.features.stubs(:posix?).returns(true)
Puppet.features.stubs(:microsoft_windows?).returns(false)
# Create a mock resource
@resource = Puppet::Type.type(:service).new(
:name => "/system/myservice", :ensure => :running, :enable => :true)

View File

@ -22,8 +22,17 @@ describe Puppet::Util::Log do
Puppet::Util::Log.setup_default
end
it "should fall back to :eventlog" do
Puppet.features.stubs(:syslog?).returns(false)
Puppet.features.stubs(:eventlog?).returns(true)
Puppet::Util::Log.expects(:newdestination).with(:eventlog)
Puppet::Util::Log.setup_default
end
it "should fall back to :file" do
Puppet.features.stubs(:syslog?).returns(false)
Puppet.features.stubs(:eventlog?).returns(false)
Puppet::Util::Log.expects(:newdestination).with(Puppet[:puppetdlog])
Puppet::Util::Log.setup_default
@ -72,6 +81,46 @@ describe Puppet::Util::Log do
end
end
describe Puppet::Util::Log::DestEventlog, :if => Puppet.features.microsoft_windows? do
require 'win32/eventlog' if Puppet.features.microsoft_windows?
before :each do
Win32::EventLog.stubs(:open).returns(mock 'mylog')
Win32::EventLog.stubs(:report_event)
Win32::EventLog.stubs(:close)
Puppet.features.stubs(:eventlog?).returns(true)
end
it "should restrict its suitability" do
Puppet.features.expects(:eventlog?).returns(false)
Puppet::Util::Log::DestEventlog.suitable?('whatever').should == false
end
it "should open the 'Application' event log" do
Win32::EventLog.expects(:open).with('Application')
Puppet::Util::Log.newdestination(:eventlog)
end
it "should close the event log" do
log = mock('myeventlog')
log.expects(:close)
Win32::EventLog.expects(:open).returns(log)
Puppet::Util::Log.newdestination(:eventlog)
Puppet::Util::Log.close(:eventlog)
end
it "should handle each puppet log level" do
log = Puppet::Util::Log::DestEventlog.new
Puppet::Util::Log.eachlevel do |level|
log.to_native(level).should be_is_a(Array)
end
end
end
describe "instances" do
before do
Puppet::Util::Log.stubs(:newmessage)

View File

@ -6,6 +6,7 @@ require 'puppet/util/network_device/transport/telnet'
describe Puppet::Util::NetworkDevice::Transport::Telnet do
before(:each) do
TCPSocket.stubs(:open).returns stub_everything('tcp')
@transport = Puppet::Util::NetworkDevice::Transport::Telnet.new()
end
@ -13,6 +14,14 @@ describe Puppet::Util::NetworkDevice::Transport::Telnet do
@transport.should_not be_handles_login
end
it "should not open any files" do
File.expects(:open).never
@transport.host = "localhost"
@transport.port = 23
@transport.connect
end
it "should connect to the given host and port" do
Net::Telnet.expects(:new).with { |args| args["Host"] == "localhost" && args["Port"] == 23 }.returns stub_everything
@transport.host = "localhost"

View File

@ -87,9 +87,14 @@ describe Puppet::Util::Settings do
# case behaviors / uses. However, until that time... we need to make sure that our private run_mode=
# setter method gets properly called during app initialization.
it "should call the hacky run mode setter method until we do a better job of separating run_mode" do
app_defaults = {}
Puppet::Util::Settings::REQUIRED_APP_SETTINGS.each do |key|
app_defaults[key] = "foo"
end
@settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS)
@settings.expects(:run_mode=).with(:user)
@settings.initialize_app_defaults(@settings.send(:app_defaults_for_tests))
@settings.expects(:run_mode=).with("foo")
@settings.initialize_app_defaults(app_defaults)
end
end