diff options
| author | Stig Sandbeck Mathisen <ssm@debian.org> | 2013-10-08 09:19:01 +0200 |
|---|---|---|
| committer | Stig Sandbeck Mathisen <ssm@debian.org> | 2013-10-08 09:19:01 +0200 |
| commit | 90ffd9c9413975b3e6af205b1962364c06128f31 (patch) | |
| tree | 77092c11cfa0805893e5b77fdaee18b15f56f5fb | |
| parent | 6a7cea9da38e05c36cfccc04de363e092b87dffb (diff) | |
| parent | ae6e887eb3b09629a5e45b827ac0ca366c3100ea (diff) | |
| download | puppet-upstream/3.3.1.tar.gz | |
Imported Upstream version 3.3.1upstream/3.3.1
59 files changed, 640 insertions, 374 deletions
diff --git a/ext/debian/changelog b/ext/debian/changelog index 13adb524f..b94c8e5b7 100644 --- a/ext/debian/changelog +++ b/ext/debian/changelog @@ -1,8 +1,8 @@ -puppet (3.3.0-1puppetlabs1) hardy lucid natty oneiric unstable sid squeeze wheezy precise; urgency=low +puppet (3.3.1-1puppetlabs1) hardy lucid natty oneiric unstable sid squeeze wheezy precise; urgency=low - * Update to version 3.3.0-1puppetlabs1 + * Update to version 3.3.1-1puppetlabs1 - -- Puppet Labs Release <info@puppetlabs.com> Thu, 12 Sep 2013 13:57:55 -0700 + -- Puppet Labs Release <info@puppetlabs.com> Mon, 07 Oct 2013 11:00:57 -0700 puppet (3.2.3-0.1rc0puppetlabs1) lucid unstable sid squeeze wheezy precise quantal raring; urgency=low diff --git a/ext/ips/puppet.p5m b/ext/ips/puppet.p5m index 64cacf8f4..06cb710ff 100644 --- a/ext/ips/puppet.p5m +++ b/ext/ips/puppet.p5m @@ -1,6 +1,6 @@ -set name=pkg.fmri value=pkg://puppetlabs.com/system/management/puppet@3.3.0,12.4.1-0 +set name=pkg.fmri value=pkg://puppetlabs.com/system/management/puppet@3.3.1,12.5.0-0 set name=pkg.summary value="Puppet, an automated configuration management tool" -set name=pkg.human-version value="3.3.0" +set name=pkg.human-version value="3.3.1" set name=pkg.description value="Puppet, an automated configuration management tool" set name=info.classification value="org.opensolaris.category.2008:System/Administration and Configuration" set name=org.opensolaris.consolidation value="puppet" diff --git a/ext/redhat/puppet.spec b/ext/redhat/puppet.spec index 2d9bccada..b92662873 100644 --- a/ext/redhat/puppet.spec +++ b/ext/redhat/puppet.spec @@ -16,8 +16,8 @@ %endif # VERSION is subbed out during rake srpm process -%global realversion 3.3.0 -%global rpmversion 3.3.0 +%global realversion 3.3.1 +%global rpmversion 3.3.1 %global confdir ext/redhat @@ -391,8 +391,8 @@ fi rm -rf %{buildroot} %changelog -* Thu Sep 12 2013 Puppet Labs Release <info@puppetlabs.com> - 3.3.0-1 -- Build for 3.3.0 +* Mon Oct 07 2013 Puppet Labs Release <info@puppetlabs.com> - 3.3.1-1 +- Build for 3.3.1 * Thu Jun 27 2013 Matthaus Owens <matthaus@puppetlabs.com> - 3.2.3-0.1rc0 - Bump requires on ruby-rgen to 0.6.5 diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 921015750..6547f7458 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -142,6 +142,7 @@ class Puppet::Configurer init_storage Puppet::Util::Log.newdestination(report) + begin unless Puppet[:node_name_fact].empty? query_options = get_facts(options) @@ -210,13 +211,6 @@ class Puppet::Configurer Puppet::Transaction::Report.indirection.save(report, nil, :environment => @environment) if Puppet[:report] rescue => detail Puppet.log_exception(detail, "Could not send report: #{detail}") - if detail.message =~ /Could not intern from pson.*Puppet::Transaction::Report/ - Puppet.notice("There was an error sending the report.") - Puppet.notice("This error is possibly caused by sending the report in a format the master can not handle.") - Puppet.notice("A puppet master older than 3.2.2 can not handle pson reports.") - Puppet.notice("Set report_serialization_format=yaml on the agent to send reports to older masters.") - Puppet.notice("See http://links.puppetlabs.com/deprecate_yaml_on_network for more information.") - end end def save_last_run_summary(report) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 793e88c29..7a161fc57 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1092,12 +1092,24 @@ EOT This should almost always be set to `pson`. It can be temporarily set to `yaml` to let agents using this Puppet version connect to a puppet master - running Puppet 3.0.0 through 3.2.4.", - :hook => proc do |value| - if value == "yaml" - Puppet.deprecation_warning("Sending reports in 'yaml' is deprecated; use 'pson' instead.") - end - end + running Puppet 3.0.0 through 3.2.x. + + Note that this is set to 'yaml' automatically if the agent detects an + older master, so should never need to be set explicitly." + }, + :legacy_query_parameter_serialization => { + :default => false, + :type => :boolean, + :desc => "The serialization format to use when sending file_metadata + query parameters. Older versions of puppet master expect certain query + parameters to be serialized as yaml, which is deprecated. + + This should almost always be false. It can be temporarily set to true + to let agents using this Puppet version connect to a puppet master + running Puppet 3.0.0 through 3.2.x. + + Note that this is set to true automatically if the agent detects an + older master, so should never need to be set explicitly." }, :agent_catalog_run_lockfile => { :default => "$statedir/agent_catalog_run.lock", diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index cd9f5ab5a..15e1cd76b 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -14,12 +14,16 @@ class Puppet::FileBucket::File attr :bucket_path def self.supported_formats + [:s, :pson] + end + + def self.default_format # This should really be :raw, like is done for Puppet::FileServing::Content # but this class hasn't historically supported `from_raw`, so switching # would break compatibility between newer 3.x agents talking to older 3.x # masters. However, to/from_s has been supported and achieves the desired # result without breaking compatibility. - [:s] + :s end def initialize(contents, options = {}) @@ -54,6 +58,11 @@ class Puppet::FileBucket::File self.new(contents) end + def to_pson + Puppet.deprecation_warning("Serializing Puppet::FileBucket::File objects to pson is deprecated.") + { "contents" => contents }.to_pson + end + # This method is deprecated, but cannot be removed for awhile, otherwise # older agents sending pson couldn't backup to filebuckets on newer masters def self.from_pson(pson) diff --git a/lib/puppet/forge/repository.rb b/lib/puppet/forge/repository.rb index c0c128bcc..75b7a9f4c 100644 --- a/lib/puppet/forge/repository.rb +++ b/lib/puppet/forge/repository.rb @@ -1,10 +1,13 @@ require 'net/https' -require 'zlib' require 'digest/sha1' require 'uri' require 'puppet/util/http_proxy' require 'puppet/forge/errors' +if Puppet.features.zlib? && Puppet[:zlib] + require 'zlib' +end + class Puppet::Forge # = Repository # @@ -26,9 +29,12 @@ class Puppet::Forge Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError, - Zlib::GzipFile::Error, ] + if Puppet.features.zlib? && Puppet[:zlib] + NET_HTTP_EXCEPTIONS << Zlib::GzipFile::Error + end + # Instantiate a new repository instance rooted at the +url+. # The agent will report +consumer_version+ in the User-Agent to # the repository. diff --git a/lib/puppet/graph/simple_graph.rb b/lib/puppet/graph/simple_graph.rb index 6c68ec617..57174ee67 100644 --- a/lib/puppet/graph/simple_graph.rb +++ b/lib/puppet/graph/simple_graph.rb @@ -530,7 +530,7 @@ class Puppet::Graph::SimpleGraph end def to_yaml_properties - (instance_variables + [:@vertices, :@edges] - + (super + [:@vertices, :@edges] - [:@in_to, :@out_from, :@upstream_from, :@downstream_from]).uniq end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index fd20d56e6..9f95b2f2e 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -159,9 +159,27 @@ class Puppet::Indirector::Request # Create the query string, if options are present. def query_string return "" if options.nil? || options.empty? + + # For backward compatibility with older (pre-3.3) masters, + # this puppet option allows serialization of query parameter + # arrays as yaml. This can be removed when we remove yaml + # support entirely. + if Puppet.settings[:legacy_query_parameter_serialization] + replace_arrays_with_yaml + end + "?" + encode_params(expand_into_parameters(options.to_a)) end + def replace_arrays_with_yaml + options.each do |key, value| + case value + when Array + options[key] = YAML.dump(value) + end + end + end + def expand_into_parameters(data) data.inject([]) do |params, key_value| key, value = key_value diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index c3ace6950..44c26e78b 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -98,6 +98,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end if is_http_200?(response) + check_master_version(response) content_type, body = parse_response(response) result = deserialize_find(content_type, body) result.name = request.key if result.respond_to?(:name=) @@ -112,7 +113,12 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus http_head(request, indirection2uri(request), headers) end - !!is_http_200?(response) + if is_http_200?(response) + check_master_version(response) + true + else + false + end end def search(request) @@ -121,6 +127,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end if is_http_200?(response) + check_master_version(response) content_type, body = parse_response(response) deserialize_search(content_type, body) || [] else @@ -136,6 +143,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end if is_http_200?(response) + check_master_version(response) content_type, body = parse_response(response) deserialize_destroy(content_type, body) else @@ -151,6 +159,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end if is_http_200?(response) + check_master_version(response) content_type, body = parse_response(response) deserialize_save(content_type, body) else @@ -191,6 +200,18 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus Net::HTTPError.new(message, response) end + def check_master_version response + if !response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] && + (Puppet[:legacy_query_parameter_serialization] == false || Puppet[:report_serialization_format] != "yaml") + Puppet.notice "Using less secure serialization of reports and query parameters for compatibility" + Puppet.notice "with older puppet master. To remove this notice, please upgrade your master(s) " + Puppet.notice "to Puppet 3.3 or newer." + Puppet.notice "See http://links.puppetlabs.com/deprecate_yaml_on_network for more information." + Puppet[:legacy_query_parameter_serialization] = true + Puppet[:report_serialization_format] = "yaml" + end + end + # Returns the content_type, stripping any appended charset, and the # body, decompressed if necessary (content-encoding is checked inside # uncompress_body) diff --git a/lib/puppet/indirector/status/local.rb b/lib/puppet/indirector/status/local.rb index e64c3ddb0..fc674eea9 100644 --- a/lib/puppet/indirector/status/local.rb +++ b/lib/puppet/indirector/status/local.rb @@ -5,6 +5,8 @@ class Puppet::Indirector::Status::Local < Puppet::Indirector::Code desc "Get status locally. Only used internally." def find( *anything ) - model.new + status = model.new + status.version= Puppet.version + status end end diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index c2e28391a..014994b9d 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -1,28 +1,16 @@ require 'puppet/indirector/terminus' +require 'puppet/util/yaml' # The base class for YAML indirection termini. class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus - if defined?(::Psych::SyntaxError) - YamlLoadExceptions = [::StandardError, ::ArgumentError, ::Psych::SyntaxError] - else - YamlLoadExceptions = [::StandardError, ::ArgumentError] - end - # Read a given name's file in and convert it from YAML. def find(request) file = path(request.key) return nil unless FileTest.exist?(file) - yaml = nil - begin - yaml = ::File.read(file) - rescue => detail - raise Puppet::Error, "Could not read YAML data for #{indirection.name} #{request.key}: #{detail}" - end - begin - return from_yaml(yaml) - rescue *YamlLoadExceptions => detail + return Puppet::Util::Yaml.load_file(file) + rescue Puppet::Util::Yaml::YamlLoadError => detail raise Puppet::Error, "Could not parse YAML data for #{indirection.name} #{request.key}: #{detail}" end end @@ -39,9 +27,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus Dir.mkdir(basedir) unless FileTest.exist?(basedir) begin - Puppet::Util.replace_file(file, 0660) do |f| - f.print to_yaml(request.instance) - end + Puppet::Util::Yaml.dump(request.instance, file) rescue TypeError => detail Puppet.err "Could not save #{self.name} #{request.key}: #{detail}" end diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb index 45d75dc6c..4aaeddedd 100644 --- a/lib/puppet/network/formats.rb +++ b/lib/puppet/network/formats.rb @@ -3,18 +3,28 @@ require 'puppet/network/format_handler' Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do def intern(klass, text) data = YAML.load(text, :safe => true, :deserialize_symbols => true) - return data if data.is_a?(klass) - klass.from_pson(data) + data_to_instance(klass, data) end def intern_multiple(klass, text) - YAML.load(text, :safe => true, :deserialize_symbols => true).collect do |data| - if data.is_a?(klass) - data - else - klass.from_pson(data) - end + data = YAML.load(text, :safe => true, :deserialize_symbols => true) + unless data.respond_to?(:collect) + raise Puppet::Network::FormatHandler::FormatError, "Serialized YAML did not contain a collection of instances when calling intern_multiple" + end + + data.collect do |datum| + data_to_instance(klass, datum) + end + end + + def data_to_instance(klass, data) + return data if data.is_a?(klass) + + unless data.is_a? Hash + raise Puppet::Network::FormatHandler::FormatError, "Serialized YAML did not contain a valid instance of #{klass}" end + + klass.from_pson(data) end def render(instance) diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 93f23eb8a..3519efd11 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,3 +1,4 @@ module Puppet::Network::HTTP HEADER_ENABLE_PROFILING = "X-Puppet-Profiling" + HEADER_PUPPET_VERSION = "X-Puppet-Version" end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index c9f7c991d..b8abd80e4 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -84,6 +84,8 @@ module Puppet::Network::HTTP::Handler request_method = http_method(request) request_path = path(request) + response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION] = Puppet.version + configure_profiler(request_headers, request_params) Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do @@ -97,7 +99,7 @@ module Puppet::Network::HTTP::Handler rescue SystemExit,NoMemoryError raise rescue HTTPError => e - return do_exception(response, e.message, e.status) + return do_http_control_exception(response, e) rescue Exception => e return do_exception(response, e) ensure @@ -121,11 +123,7 @@ module Puppet::Network::HTTP::Handler status = 403 if status == 400 end - if exception.is_a?(Exception) - Puppet.log_exception(exception) - else - Puppet.notice(exception.to_s) - end + Puppet.log_exception(exception) set_content_type(response, "text/plain") set_response(response, exception.to_s, status) @@ -219,6 +217,13 @@ module Puppet::Network::HTTP::Handler private + def do_http_control_exception(response, exception) + msg = exception.message + Puppet.info(msg) + set_content_type(response, "text/plain") + set_response(response, msg, exception.status) + end + def report_if_deprecated(format) if format.name == :yaml || format.name == :b64_zlib_yaml Puppet.deprecation_warning("YAML in network requests is deprecated and will be removed in a future version. See http://links.puppetlabs.com/deprecate_yaml_on_network") @@ -250,7 +255,6 @@ module Puppet::Network::HTTP::Handler def read_body_into_model(model_class, request) data = body(request).to_s - raise ArgumentError, "No data to save" if !data or data.empty? format = request_format(request) model_class.convert_from(format, data) diff --git a/lib/puppet/network/http/rack/rest.rb b/lib/puppet/network/http/rack/rest.rb index b2cfa078d..83d61866b 100644 --- a/lib/puppet/network/http/rack/rest.rb +++ b/lib/puppet/network/http/rack/rest.rb @@ -1,4 +1,5 @@ require 'openssl' +require 'cgi' require 'puppet/network/http/handler' require 'puppet/network/http/rack/httphandler' require 'puppet/util/ssl' @@ -73,7 +74,15 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler # Return the query params for this request. def params(request) - result = decode_params(request.params) + if request.post? + params = request.params + else + # rack doesn't support multi-valued query parameters, + # e.g. ignore, so parse them ourselves + params = CGI.parse(request.query_string) + convert_singular_arrays_to_value(params) + end + result = decode_params(params) result.merge(extract_client_info(request)) end @@ -125,4 +134,11 @@ class Puppet::Network::HTTP::RackREST < Puppet::Network::HTTP::RackHttpHandler result end + def convert_singular_arrays_to_value(hash) + hash.each do |key, value| + if value.size == 1 + hash[key] = value.first + end + end + end end diff --git a/lib/puppet/parser/functions/create_resources.rb b/lib/puppet/parser/functions/create_resources.rb index 054b1f70c..733dc4511 100644 --- a/lib/puppet/parser/functions/create_resources.rb +++ b/lib/puppet/parser/functions/create_resources.rb @@ -7,11 +7,11 @@ Puppet::Parser::Functions::newfunction(:create_resources, :arity => -3, :doc => # A hash of user resources: $myusers = { 'nick' => { uid => '1330', - group => allstaff, - groups => ['developers', 'operations', 'release'], } + gid => allstaff, + groups => ['developers', 'operations', 'release'], }, 'dan' => { uid => '1308', - group => allstaff, - groups => ['developers', 'prosvc', 'release'], } + gid => allstaff, + groups => ['developers', 'prosvc', 'release'], }, } create_resources(user, $myusers) diff --git a/lib/puppet/parser/yaml_trimmer.rb b/lib/puppet/parser/yaml_trimmer.rb index b9e6a260f..657ce9d59 100644 --- a/lib/puppet/parser/yaml_trimmer.rb +++ b/lib/puppet/parser/yaml_trimmer.rb @@ -2,8 +2,6 @@ module Puppet::Parser::YamlTrimmer REMOVE = [:@scope, :@source] def to_yaml_properties - r = instance_variables - REMOVE - r -= skip_for_yaml if respond_to?(:skip_for_yaml) - r + super - REMOVE end end diff --git a/lib/puppet/provider/package/dpkg.rb b/lib/puppet/provider/package/dpkg.rb index 062091bec..c8528cc4b 100755 --- a/lib/puppet/provider/package/dpkg.rb +++ b/lib/puppet/provider/package/dpkg.rb @@ -44,6 +44,7 @@ Puppet::Type.type(:package).provide :dpkg, :parent => Puppet::Provider::Package self::DPKG_DESCRIPTION_DELIMITER = ':DESC:' self::DPKG_QUERY_FORMAT_STRING = %Q{'${Status} ${Package} ${Version} #{self::DPKG_DESCRIPTION_DELIMITER} ${Description}\\n#{self::DPKG_DESCRIPTION_DELIMITER}\\n'} self::FIELDS_REGEX = %r{^(\S+) +(\S+) +(\S+) (\S+) (\S*) #{self::DPKG_DESCRIPTION_DELIMITER} (.*)$} + self::DPKG_PACKAGE_NOT_FOUND_REGEX = /no package.*match/i self::FIELDS= [:desired, :error, :status, :name, :ensure, :description] self::END_REGEX = %r{^#{self::DPKG_DESCRIPTION_DELIMITER}$} @@ -59,7 +60,7 @@ Puppet::Type.type(:package).provide :dpkg, :parent => Puppet::Provider::Package line = pipe.gets unless hash = parse_line(line) - Puppet.warning "Failed to match dpkg-query line #{line.inspect}" + Puppet.warning "Failed to match dpkg-query line #{line.inspect}" if !self::DPKG_PACKAGE_NOT_FOUND_REGEX.match(line) return nil end diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb index 910669b46..e99e51c7b 100755 --- a/lib/puppet/provider/package/rpm.rb +++ b/lib/puppet/provider/package/rpm.rb @@ -13,6 +13,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr # The query format by which we identify installed packages self::NEVRA_FORMAT = %Q{'%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} #{self::RPM_DESCRIPTION_DELIMITER} %{SUMMARY}\\n'} self::NEVRA_REGEX = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)(?: #{self::RPM_DESCRIPTION_DELIMITER} ?(.*))?$} + self::RPM_PACKAGE_NOT_FOUND_REGEX = /package .+ is not installed/ self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch, :description] commands :rpm => "rpm" @@ -76,7 +77,6 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr rescue Puppet::ExecutionFailure return nil end - # FIXME: We could actually be getting back multiple packages # for multilib and this will only return the first such package @property_hash.update(self.class.nevra_to_hash(output)) @@ -150,7 +150,9 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr line.strip! hash = {} - if match = self::NEVRA_REGEX.match(line) + if self::RPM_PACKAGE_NOT_FOUND_REGEX.match(line) + # pass through, package was not found + elsif match = self::NEVRA_REGEX.match(line) self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v } hash[:provider] = self.name hash[:ensure] = "#{hash[:version]}-#{hash[:release]}" diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb index e922d9381..1a13c8df3 100755 --- a/lib/puppet/provider/parsedfile.rb +++ b/lib/puppet/provider/parsedfile.rb @@ -64,13 +64,15 @@ class Puppet::Provider::ParsedFile < Puppet::Provider return unless defined?(@modified) and ! @modified.empty? flushed = [] - @modified.sort { |a,b| a.to_s <=> b.to_s }.uniq.each do |target| - Puppet.debug "Flushing #{@resource_type.name} provider target #{target}" - flush_target(target) - flushed << target + begin + @modified.sort { |a,b| a.to_s <=> b.to_s }.uniq.each do |target| + Puppet.debug "Flushing #{@resource_type.name} provider target #{target}" + flushed << target + flush_target(target) + end + ensure + @modified.reject! { |t| flushed.include?(t) } end - - @modified.reject! { |t| flushed.include?(t) } end # Make sure our file is backed up, but only back it up once per transaction. @@ -92,6 +94,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider records = target_records(target).reject { |r| r[:ensure] == :absent } + target_object(target).write(to_file(records)) end diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb index 24332a599..6bfe73b2b 100644 --- a/lib/puppet/resource/status.rb +++ b/lib/puppet/resource/status.rb @@ -135,7 +135,7 @@ module Puppet end def to_yaml_properties - YAML_ATTRIBUTES & instance_variables + YAML_ATTRIBUTES & super end private diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb index 55bcc3d41..9ad55668b 100644 --- a/lib/puppet/status.rb +++ b/lib/puppet/status.rb @@ -29,4 +29,12 @@ class Puppet::Status def name=(name) # NOOP end + + def version + @status['version'] + end + + def version=(version) + @status['version'] = version + end end diff --git a/lib/puppet/transaction/additional_resource_generator.rb b/lib/puppet/transaction/additional_resource_generator.rb index 60a2af2eb..ef59ce2ef 100644 --- a/lib/puppet/transaction/additional_resource_generator.rb +++ b/lib/puppet/transaction/additional_resource_generator.rb @@ -38,7 +38,7 @@ class Puppet::Transaction::AdditionalResourceGenerator generated = replace_duplicates_with_catalog_resources(resource.eval_generate) return false if generated.empty? rescue => detail - resource.log_exception(detail, "Failed to generate additional resources using 'eval_generate: #{detail}") + resource.log_exception(detail, "Failed to generate additional resources using 'eval_generate': #{detail}") return false end add_resources(generated, resource) diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb index 0af7a8f63..c832665c6 100644 --- a/lib/puppet/transaction/event.rb +++ b/lib/puppet/transaction/event.rb @@ -83,7 +83,7 @@ class Puppet::Transaction::Event end def to_yaml_properties - YAML_ATTRIBUTES & instance_variables + YAML_ATTRIBUTES & super end private diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 7223a5fba..92e685f42 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -312,11 +312,15 @@ class Puppet::Transaction::Report # @api private # def to_yaml_properties - instance_variables - [:@external_times] + super - [:@external_times] end def self.supported_formats - [Puppet[:report_serialization_format].intern] + [:pson, :yaml] + end + + def self.default_format + Puppet[:report_serialization_format].intern end private diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb index 194331c01..b593ae3d3 100644 --- a/lib/puppet/util/monkey_patches.rb +++ b/lib/puppet/util/monkey_patches.rb @@ -161,58 +161,6 @@ class Range alias_method :&, :intersection unless method_defined? :& end -######################################################################## -# The return type of `instance_variables` changes between Ruby 1.8 and 1.9 -# releases; it used to return an array of strings in the form "@foo", but -# now returns an array of symbols in the form :@foo. -# -# Nothing else in the stack cares which form you get - you can pass the -# string or symbol to things like `instance_variable_set` and they will work -# transparently. -# -# Having the same form in all releases of Puppet is a win, though, so we -# pick a unification and enforce than on all releases. That way developers -# who do set math on them (eg: for YAML rendering) don't have to handle the -# distinction themselves. -# -# In the sane tradition, we bring older releases into conformance with newer -# releases, so we return symbols rather than strings, to be more like the -# future versions of Ruby are. -# -# We also carefully support reloading, by only wrapping when we don't -# already have the original version of the method aliased away somewhere. -if RUBY_VERSION[0,3] == '1.8' - unless Object.respond_to?(:puppet_original_instance_variables) - - # Add our wrapper to the method. - class Object - alias :puppet_original_instance_variables :instance_variables - - def instance_variables - puppet_original_instance_variables.map(&:to_sym) - end - end - - # The one place that Ruby 1.8 assumes something about the return format of - # the `instance_variables` method is actually kind of odd, because it uses - # eval to get at instance variables of another object. - # - # This takes the original code and applies replaces instance_eval with - # instance_variable_get through it. All other bugs in the original (such - # as equality depending on the instance variables having the same order - # without any promise from the runtime) are preserved. --daniel 2012-03-11 - require 'resolv' - class Resolv::DNS::Resource - def ==(other) # :nodoc: - return self.class == other.class && - self.instance_variables == other.instance_variables && - self.instance_variables.collect {|name| self.instance_variable_get name} == - other.instance_variables.collect {|name| other.instance_variable_get name} - end - end - end -end - # (#19151) Reject all SSLv2 ciphers and handshakes require 'openssl' class OpenSSL::SSL::SSLContext diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb index c56cdd1de..32d744862 100644 --- a/lib/puppet/util/storage.rb +++ b/lib/puppet/util/storage.rb @@ -1,6 +1,7 @@ require 'yaml' require 'sync' require 'singleton' +require 'puppet/util/yaml' # a class for storing state class Puppet::Util::Storage @@ -32,12 +33,10 @@ class Puppet::Util::Storage def self.clear @@state.clear - Storage.init end def self.init @@state = {} - @@splitchar = "\t" end self.init @@ -47,7 +46,7 @@ class Puppet::Util::Storage filename = Puppet[:statefile] unless File.exists?(filename) - self.init unless !@@state.nil? + self.init if @@state.nil? return end unless File.file?(filename) @@ -56,8 +55,8 @@ class Puppet::Util::Storage end Puppet::Util.benchmark(:debug, "Loaded state") do begin - @@state = YAML.load(::File.read(filename)) - rescue => detail + @@state = Puppet::Util::Yaml.load_file(filename) + rescue Puppet::Util::Yaml::YamlLoadError => detail Puppet.err "Checksumfile #{filename} is corrupt (#{detail}); replacing" begin @@ -84,9 +83,7 @@ class Puppet::Util::Storage Puppet.info "Creating state file #{Puppet[:statefile]}" unless FileTest.exist?(Puppet[:statefile]) Puppet::Util.benchmark(:debug, "Stored state") do - Puppet::Util.replace_file(Puppet[:statefile], 0660) do |fh| - fh.print YAML.dump(@@state) - end + Puppet::Util::Yaml.dump(@@state, Puppet[:statefile]) end end end diff --git a/lib/puppet/util/yaml.rb b/lib/puppet/util/yaml.rb new file mode 100644 index 000000000..d0c37ae77 --- /dev/null +++ b/lib/puppet/util/yaml.rb @@ -0,0 +1,23 @@ +require 'yaml' + +module Puppet::Util::Yaml + if defined?(::Psych::SyntaxError) + YamlLoadExceptions = [::StandardError, ::Psych::SyntaxError] + else + YamlLoadExceptions = [::StandardError] + end + + class YamlLoadError < Puppet::Error; end + + def self.load_file(filename) + YAML.load_file(filename) + rescue *YamlLoadExceptions => detail + raise YamlLoadError.new(detail.message, detail) + end + + def self.dump(structure, filename) + Puppet::Util.replace_file(filename, 0660) do |fh| + YAML.dump(structure, fh) + end + end +end diff --git a/lib/puppet/util/zaml.rb b/lib/puppet/util/zaml.rb index 1915d1468..abd36a033 100644 --- a/lib/puppet/util/zaml.rb +++ b/lib/puppet/util/zaml.rb @@ -171,16 +171,28 @@ end ################################################################ class Object - def to_yaml_properties - instance_variables # default YAML behaviour. + # Users of this method need to do set math consistently with the + # result. Since #instance_variables returns strings in 1.8 and symbols + # on 1.9, standardize on symbols + if RUBY_VERSION[0,3] == '1.8' + def to_yaml_properties + instance_variables.map(&:to_sym) + end + else + def to_yaml_properties + instance_variables + end end + def yaml_property_munge(x) x end + def zamlized_class_name(root) cls = self.class "!ruby/#{root.name.downcase}#{cls == root ? '' : ":#{cls.respond_to?(:name) ? cls.name : cls}"}" end + def to_zaml(z) z.first_time_only(self) { z.emit(zamlized_class_name(Object)) diff --git a/lib/puppet/version.rb b/lib/puppet/version.rb index b0ace7de6..9f9f738be 100644 --- a/lib/puppet/version.rb +++ b/lib/puppet/version.rb @@ -7,7 +7,7 @@ module Puppet - PUPPETVERSION = '3.3.0' + PUPPETVERSION = '3.3.1' ## # version is a public API method intended to always provide a fast and diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index 6fa71a11d..5377251fd 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1004,8 +1004,8 @@ describe Puppet::Type.type(:file) do catalog.add_resource file catalog.apply - get_owner(path).should == 'S-1-5-32-544' - get_group(path).should == 'S-1-0-0' + get_owner(path).should =~ /^S\-1\-5\-.*$/ + get_group(path).should =~ /^S\-1\-0\-0.*$/ get_mode(path).should == 0644 end end diff --git a/spec/lib/puppet_spec/matchers.rb b/spec/lib/puppet_spec/matchers.rb index 5725f4b6d..67a5f4779 100644 --- a/spec/lib/puppet_spec/matchers.rb +++ b/spec/lib/puppet_spec/matchers.rb @@ -103,3 +103,13 @@ RSpec::Matchers.define :equal_attributes_of do |expected| end end end + +RSpec::Matchers.define :be_one_of do |*expected| + match do |actual| + expected.include? actual + end + + failure_message_for_should do |actual| + "expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}" + end +end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index 83524eae8..536fb47de 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -20,6 +20,8 @@ end describe Puppet::Agent do before do + Puppet::Status.indirection.stubs(:find).returns Puppet::Status.new("version" => Puppet.version) + @agent = Puppet::Agent.new(AgentTestClient, false) # So we don't actually try to hit the filesystem. diff --git a/spec/unit/file_bucket/file_spec.rb b/spec/unit/file_bucket/file_spec.rb index 9add6d52c..0269142a1 100755 --- a/spec/unit/file_bucket/file_spec.rb +++ b/spec/unit/file_bucket/file_spec.rb @@ -15,18 +15,28 @@ describe Puppet::FileBucket::File do let(:bucketdir) { Puppet[:bucketdir] = tmpdir('bucket') } let(:destdir) { File.join(bucketdir, "8/b/3/7/0/2/a/d/#{digest}") } - it "defines its supported format to be `:s`" do - expect(Puppet::FileBucket::File.supported_formats).to eq([:s]) + it "defaults to serializing to `:s`" do + expect(Puppet::FileBucket::File.default_format).to eq(:s) end - it "serializes to `:s`" do - expect(Puppet::FileBucket::File.new(contents).to_s).to eq(contents) + it "accepts s and pson" do + expect(Puppet::FileBucket::File.supported_formats).to include(:s, :pson) end - it "deserializes from `:s`" do - file = Puppet::FileBucket::File.from_s(contents) + it "can make a round trip through `s`" do + file = Puppet::FileBucket::File.new(contents) - expect(file.contents).to eq(contents) + tripped = Puppet::FileBucket::File.convert_from(:s, file.render) + + expect(tripped.contents).to eq(contents) + end + + it "can make a round trip through `pson`" do + file = Puppet::FileBucket::File.new(contents) + + tripped = Puppet::FileBucket::File.convert_from(:pson, file.render(:pson)) + + expect(tripped.contents).to eq(contents) end it "should raise an error if changing content" do @@ -76,12 +86,8 @@ describe Puppet::FileBucket::File do end it "should convert the contents to PSON" do - # The model class no longer defines to_pson and it is not a supported - # format, but pson monkey patches Object#to_pson to return - # Object#to_s.to_pson, and it monkey patches String#to_pson to wrap the - # returned string in quotes. So it works in a way that is completely - # unexpected, and it doesn't round-trip correctly, awesome. - Puppet::FileBucket::File.new("file contents").to_pson.should == '"file contents"' + Puppet.expects(:deprecation_warning).with('Serializing Puppet::FileBucket::File objects to pson is deprecated.') + Puppet::FileBucket::File.new("file contents").to_pson.should == '{"contents":"file contents"}' end it "should load from PSON" do diff --git a/spec/unit/indirector/certificate/rest_spec.rb b/spec/unit/indirector/certificate/rest_spec.rb index dcea747f9..c8f304a67 100755 --- a/spec/unit/indirector/certificate/rest_spec.rb +++ b/spec/unit/indirector/certificate/rest_spec.rb @@ -51,6 +51,7 @@ rn/G response = stub 'response', :code => "200", :body => cert_string response.stubs(:[]).with('content-type').returns "text/plain" response.stubs(:[]).with('content-encoding') + response.stubs(:[]).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION).returns(Puppet.version) network.stubs(:verify_callback=) network.expects(:get).returns response diff --git a/spec/unit/indirector/report/rest_spec.rb b/spec/unit/indirector/report/rest_spec.rb index f2c443acf..22dfb0f0b 100755 --- a/spec/unit/indirector/report/rest_spec.rb +++ b/spec/unit/indirector/report/rest_spec.rb @@ -36,6 +36,7 @@ describe Puppet::Transaction::Report::Rest do obj = stub('http 200 ok', :code => code.to_s, :body => body) obj.stubs(:[]).with('content-type').returns(content_type) obj.stubs(:[]).with('content-encoding').returns(encoding) + obj.stubs(:[]).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION).returns(Puppet.version) obj end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index 46de7c99d..c43c46709 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -334,6 +334,15 @@ describe Puppet::Indirector::Request do } end + it "should convert an array of values into a single yaml entry when in legacy mode" do + Puppet[:legacy_query_parameter_serialization] = true + request = a_request_with_options(:one => %w{one two}) + + the_parsed_query_string_from(request).should == { + "one" => ["--- \n - one\n - two"] + } + end + it "should stringify simple data types inside an array" do request = a_request_with_options(:one => ['one', nil]) diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb index 8a3fbca7f..787c4cb5e 100755 --- a/spec/unit/indirector/rest_spec.rb +++ b/spec/unit/indirector/rest_spec.rb @@ -4,9 +4,30 @@ require 'puppet/indirector' require 'puppet/indirector/errors' require 'puppet/indirector/rest' -# Just one from each category since the code makes no real distinctions HTTP_ERROR_CODES = [300, 400, 500] + +# Just one from each category since the code makes no real distinctions shared_examples_for "a REST terminus method" do |terminus_method| + describe "when talking to an older master" do + it "should set backward compatibility settings" do + response.stubs(:[]).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION).returns nil + + terminus.send(terminus_method, request) + Puppet[:report_serialization_format].should == 'yaml' + Puppet[:legacy_query_parameter_serialization].should == true + end + end + + describe "when talking to a 3.3.1 master" do + it "should not set backward compatibility settings" do + response.stubs(:[]).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION).returns "3.3.1" + + terminus.send(terminus_method, request) + Puppet[:report_serialization_format].should == 'pson' + Puppet[:legacy_query_parameter_serialization].should == false + end + end + HTTP_ERROR_CODES.each do |code| describe "when the response code is #{code}" do let(:response) { mock_response(code, 'error messaged!!!') } @@ -115,6 +136,7 @@ describe Puppet::Indirector::REST do obj = stub('http 200 ok', :code => code.to_s, :body => body) obj.stubs(:[]).with('content-type').returns(content_type) obj.stubs(:[]).with('content-encoding').returns(encoding) + obj.stubs(:[]).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION).returns(Puppet.version) obj end @@ -174,7 +196,6 @@ describe Puppet::Indirector::REST do terminus_class.port.should == 543 end - it 'should default to :puppet for the srv_service' do Puppet::Indirector::REST.srv_service.should == :puppet end diff --git a/spec/unit/indirector/run/local_spec.rb b/spec/unit/indirector/run/local_spec.rb index 41a4fd173..ffb5d8332 100755 --- a/spec/unit/indirector/run/local_spec.rb +++ b/spec/unit/indirector/run/local_spec.rb @@ -4,11 +4,13 @@ require 'spec_helper' require 'puppet/indirector/run/local' describe Puppet::Run::Local do - it "should be a sublcass of Puppet::Indirector::Code" do + it "should be a subclass of Puppet::Indirector::Code" do Puppet::Run::Local.superclass.should equal(Puppet::Indirector::Code) end it "should call runner.run on save and return the runner" do + Puppet::Status.indirection.stubs(:find).returns Puppet::Status.new + runner = Puppet::Run.new runner.stubs(:run).returns(runner) diff --git a/spec/unit/indirector/status/local_spec.rb b/spec/unit/indirector/status/local_spec.rb new file mode 100644 index 000000000..e1f427f19 --- /dev/null +++ b/spec/unit/indirector/status/local_spec.rb @@ -0,0 +1,11 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +require 'puppet/indirector/status/local' + +describe Puppet::Indirector::Status::Local do + it "should set the puppet version" do + Puppet::Status.indirection.terminus_class = :local + expect(Puppet::Status.indirection.find('*').version).to eq(Puppet.version) + end +end diff --git a/spec/unit/indirector/status/rest_spec.rb b/spec/unit/indirector/status/rest_spec.rb index 7a794ff21..24361e24c 100755 --- a/spec/unit/indirector/status/rest_spec.rb +++ b/spec/unit/indirector/status/rest_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'puppet/indirector/status/rest' describe Puppet::Indirector::Status::Rest do - it "should be a sublcass of Puppet::Indirector::REST" do + it "should be a subclass of Puppet::Indirector::REST" do Puppet::Indirector::Status::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb index b1be5166d..8a2753479 100755 --- a/spec/unit/indirector/yaml_spec.rb +++ b/spec/unit/indirector/yaml_spec.rb @@ -4,6 +4,12 @@ require 'spec_helper' require 'puppet/indirector/yaml' describe Puppet::Indirector::Yaml do + include PuppetSpec::Files + + class TestSubject + attr_accessor :name + end + before :all do @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil Puppet::Indirector::Indirection.expects(:instance).with(:my_yaml).returns(@indirection) @@ -16,11 +22,10 @@ describe Puppet::Indirector::Yaml do before :each do @store = @store_class.new - @subject = Object.new - @subject.singleton_class.send(:attr_accessor, :name) + @subject = TestSubject.new @subject.name = :me - @dir = File.expand_path("/what/ever") + @dir = tmpdir("yaml_indirector") Puppet[:clientyamldir] = @dir Puppet.run_mode.stubs(:master?).returns false @@ -87,54 +92,25 @@ describe Puppet::Indirector::Yaml do @request.stubs(:instance).returns Object.new proc { @store.save(@request) }.should raise_error(ArgumentError) end - - it "should convert Ruby objects to YAML and write them to disk using a write lock" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(File.dirname(path)).returns(true) - Puppet::Util.expects(:replace_file).with(path, 0660).yields(file) - file.expects(:print).with(yaml) - - @store.save(@request) - end - - it "should create the indirection subdirectory if it does not exist" do - yaml = @subject.to_yaml - file = mock 'file' - path = @store.send(:path, @subject.name) - dir = File.dirname(path) - - FileTest.expects(:exist?).with(dir).returns(false) - Dir.expects(:mkdir).with(dir) - - Puppet::Util.expects(:replace_file).yields(file) - file.expects(:print).with(yaml) - - @store.save(@request) - end end describe "when retrieving YAML" do it "should read YAML in from disk and convert it to Ruby objects" do - path = @store.send(:path, @subject.name) - yaml = @subject.to_yaml - - FileTest.expects(:exist?).with(path).returns true - File.expects(:read).with(path).returns yaml + @store.save(Puppet::Indirector::Request.new(:my_yaml, :save, "testing", @subject)) - @store.find(@request).instance_variable_get("@name").should == :me + @store.find(Puppet::Indirector::Request.new(:my_yaml, :find, "testing", nil)).name.should == :me end it "should fail coherently when the stored YAML is invalid" do - path = @store.send(:path, @subject.name) - FileTest.expects(:exist?).with(path).returns(true) + saved_structure = Struct.new(:name).new("testing") - # Something that will fail in yaml - File.expects(:read).returns "--- foo:\n 1,2,3\nargh" + @store.save(Puppet::Indirector::Request.new(:my_yaml, :save, "testing", saved_structure)) + File.open(@store.path(saved_structure.name), "w") do |file| + file.puts "{ invalid" + end expect { - @store.find(@request) + @store.find(Puppet::Indirector::Request.new(:my_yaml, :find, "testing", nil)) }.to raise_error(Puppet::Error, /Could not parse YAML data/) end end diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb index 6b96b2b7d..8531a541a 100755 --- a/spec/unit/network/formats_spec.rb +++ b/spec/unit/network/formats_spec.rb @@ -63,27 +63,31 @@ describe "Puppet Network Format" do @yaml.intern(String, YAML.dump(:foo)).should == "foo" end - it "should fail when type does not match deserialized form and has no from_pson" do - expect do - @yaml.intern(Hash, YAML.dump("foo")) - end.to raise_error(NoMethodError) - end - it "should load from yaml when deserializing an array" do text = YAML.dump(["foo"]) @yaml.intern_multiple(String, text).should == ["foo"] end - it "should fail when one element does not have a from_pson" do + it "fails intelligibly instead of calling to_pson with something other than a hash" do + expect do + @yaml.intern(Puppet::Node, '') + end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a valid instance/) + end + + it "fails intelligibly when intern_multiple is called and yaml doesn't decode to an array" do expect do - @yaml.intern_multiple(Hash, YAML.dump(["foo"])) - end.to raise_error(NoMethodError) + @yaml.intern_multiple(Puppet::Node, '') + end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a collection/) + end + + it "fails intelligibly instead of calling to_pson with something other than a hash when interning multiple" do + expect do + @yaml.intern_multiple(Puppet::Node, YAML.dump(["hello"])) + end.to raise_error(Puppet::Network::FormatHandler::FormatError, /did not contain a valid instance/) end end describe "base64 compressed yaml", :if => Puppet.features.zlib? do - yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) - before do @yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) end @@ -294,6 +298,12 @@ describe "Puppet Network Format" do PsonTest.expects(:from_pson).with("baz").returns "BAZ" @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} end + + it "fails intelligibly when given invalid data" do + expect do + @pson.intern(Puppet::Node, '') + end.to raise_error(PSON::ParserError, /source did not contain any PSON/) + end end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 6d16bd799..0ff087b02 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -17,6 +17,10 @@ describe Puppet::Network::HTTP::Handler do @data = data end + def self.from_raw(raw) + new(nil, raw) + end + def self.from_pson(pson) new(pson["name"], pson["data"]) end @@ -33,7 +37,6 @@ describe Puppet::Network::HTTP::Handler do end end - # The subclass must not be all caps even though the superclass is class Puppet::TestModel::Memory < Puppet::Indirector::Memory end @@ -146,7 +149,11 @@ describe Puppet::Network::HTTP::Handler do end describe "when processing a request" do - let(:response) { mock('http response') } + let(:response) do + obj = stub "http 200 ok" + obj.stubs(:[]=).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION, Puppet.version) + obj + end before do handler.stubs(:check_authorization) @@ -230,13 +237,13 @@ describe Puppet::Network::HTTP::Handler do it "should set the format to text/plain when serializing an exception" do handler.expects(:set_content_type).with(response, "text/plain") - handler.do_exception(response, "A test", 404) + handler.do_exception(response, Exception.new("A test"), 404) end it "sends an exception string with the given status" do handler.expects(:set_response).with(response, "A test", 404) - handler.do_exception(response, "A test", 404) + handler.do_exception(response, Exception.new("A test"), 404) end it "sends an exception error with the exception's status" do @@ -249,6 +256,16 @@ describe Puppet::Network::HTTP::Handler do handler.process(request, response) end + it "logs an HTTP response exception at info level (most are harmless)" do + data = Puppet::TestModel.new("not_found", "not found") + error = Puppet::Network::HTTP::Handler::HTTPNotFoundError.new("Could not find test_model not_found") + + request = a_request_that_finds(data, :accept_header => "pson") + Puppet.expects(:info).with(error.message) + + handler.process(request, response) + end + it "should raise an error if the request is formatted in an unknown format" do handler.stubs(:content_type_header).returns "unknown format" lambda { handler.request_format(request) }.should raise_error @@ -429,12 +446,23 @@ describe Puppet::Network::HTTP::Handler do end describe "when saving a model instance" do - it "should fail to save model if data is not specified" do - data = Puppet::TestModel.new("my data", "some data") + it "allows an empty body when the format supports it" do + class Puppet::TestModel::Nonvalidatingmemory < Puppet::TestModel::Memory + def validate_key(_) + # nothing + end + end + + indirection.terminus_class = :nonvalidatingmemory + + data = Puppet::TestModel.new("test", '') request = a_request_that_submits(data) + request[:content_type_header] = "application/x-raw" request[:body] = '' - expect { handler.do_save("my_handler", "my_result", {}, request, response) }.to raise_error(ArgumentError) + handler.do_save(indirection.name, "test", {}, request, response) + + Puppet::TestModel.indirection.find("test").data.should == '' end it "saves the data sent in the request" do diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index 729c6c21a..e9527a460 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -148,6 +148,25 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do result[:bar].should == "xyzzy" end + it "should return multi-values params as an array of the values" do + req = mk_req('/?foo=baz&foo=xyzzy') + result = @handler.params(req) + result[:foo].should == ["baz", "xyzzy"] + end + + it "should return parameters from the POST body" do + req = mk_req("/", :method => 'POST', :input => 'foo=baz&bar=xyzzy') + result = @handler.params(req) + result[:foo].should == "baz" + result[:bar].should == "xyzzy" + end + + it "should not return multi-valued params in a POST body as an array of values" do + req = mk_req("/", :method => 'POST', :input => 'foo=baz&foo=xyzzy') + result = @handler.params(req) + result[:foo].should be_one_of("baz", "xyzzy") + end + it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") req = mk_req("/?foo=#{encoding}") diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 754be7b5a..7b2d73b8a 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -143,7 +143,7 @@ describe Puppet::Node::Environment do it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true - two = File.expand_path(File.join(Dir.getwd, "two")) + two = File.expand_path("two") env.validate_dirs([@path_one, 'two']).should == [@path_one, two] end end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index 24bb95005..2e84216cb 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -70,9 +70,8 @@ describe Puppet::Parser::Files do it "should accept relative templatedirs" do FileTest.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" - # We expand_path to normalize backslashes and slashes on Windows - File.expects(:directory?).with(File.expand_path(File.join(Dir.getwd,"my/templates"))).returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path(File.join(Dir.getwd,"my/templates/mytemplate")) + File.expects(:directory?).with(File.expand_path("my/templates")).returns(true) + Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path("my/templates/mytemplate") end it "should use the environment templatedir if no module is found and an environment is specified" do diff --git a/spec/unit/pops/adaptable_spec.rb b/spec/unit/pops/adaptable_spec.rb index e9b5b80d0..544a6c54f 100644 --- a/spec/unit/pops/adaptable_spec.rb +++ b/spec/unit/pops/adaptable_spec.rb @@ -92,8 +92,6 @@ describe Puppet::Pops::Adaptable::Adapter do a.value = 10 b = Farm::FarmAdapter.get(d) b.value.should == 10 - # Test implementation detail - d.instance_variables.include?(:@Farm_FarmAdapter).should == true end it "should be able to clear the adapter" do diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb index 6990ef454..cc0d88b02 100755 --- a/spec/unit/provider/package/dpkg_spec.rb +++ b/spec/unit/provider/package/dpkg_spec.rb @@ -49,7 +49,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi end it "should have documentation" do - provider_class.doc.should be_instance_of(String) + expect(provider_class.doc).to be_instance_of(String) end describe "when listing all instances" do @@ -66,7 +66,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi installed = mock 'bash' provider_class.expects(:new).with(:ensure => "4.2-5ubuntu3", :error => "ok", :desired => "install", :name => "bash", :status => "installed", :description => "GNU Bourne Again SHell", :provider => :dpkg).returns installed - provider_class.instances.should == [installed] + expect(provider_class.instances).to eq([installed]) end it "should parse multiple dpkg-query multi-line entries in the output" do @@ -77,7 +77,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi vim = mock 'vim' provider_class.expects(:new).with(:ensure => "2:7.3.547-6ubuntu5", :error => "ok", :desired => "install", :name => "vim", :status => "installed", :description => "Vi IMproved - enhanced vi editor", :provider => :dpkg).returns vim - provider_class.instances.should == [bash, vim] + expect(provider_class.instances).to eq([bash, vim]) end it "should warn on and ignore any lines it does not understand" do @@ -86,7 +86,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi Puppet.expects(:warning) provider_class.expects(:new).never - provider_class.instances.should == [] + expect(provider_class.instances).to eq([]) end it "should not warn on extra multiline description lines which we are ignoring" do @@ -105,7 +105,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi vim = mock 'vim' provider_class.expects(:new).twice.returns(bash, vim) - provider_class.instances.should == [bash, vim] + expect(provider_class.instances).to eq([bash, vim]) end it "should warn on a broken entry while still parsing a good one" do @@ -122,7 +122,7 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi saved = mock('saved') provider_class.expects(:new).twice.returns(bash, vim) - provider_class.instances.should == [bash, vim] + expect(provider_class.instances).to eq([bash, vim]) end end @@ -148,25 +148,25 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi it "should consider the package purged if dpkg-query fails" do Puppet::Util::Execution.expects(:execpipe).with(query_args).raises Puppet::ExecutionFailure.new("eh") - provider.query[:ensure].should == :purged + expect(provider.query[:ensure]).to eq(:purged) end it "should return a hash of the found package status for an installed package" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields bash_installed_io - provider.query.should == {:ensure => "4.2-5ubuntu3", :error => "ok", :desired => "install", :name => "bash", :status => "installed", :provider => :dpkg, :description => "GNU Bourne Again SHell"} + expect(provider.query).to eq({:ensure => "4.2-5ubuntu3", :error => "ok", :desired => "install", :name => "bash", :status => "installed", :provider => :dpkg, :description => "GNU Bourne Again SHell"}) end it "should consider the package absent if the dpkg-query result cannot be interpreted" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields StringIO.new("somebaddata") - provider.query[:ensure].should == :absent + expect(provider.query[:ensure]).to eq(:absent) end it "should fail if an error is discovered" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("ok", "error") - lambda { provider.query }.should raise_error(Puppet::Error) + expect { provider.query }.to raise_error(Puppet::Error) end it "should consider the package purged if it is marked 'not-installed'" do @@ -174,32 +174,32 @@ install ok installed vim 2:7.3.547-6ubuntu5 :DESC: Vi IMproved - enhanced vi edi not_installed_bash.gsub!(bash_version, "") Puppet::Util::Execution.expects(:execpipe).with(query_args).yields StringIO.new(not_installed_bash) - provider.query[:ensure].should == :purged + expect(provider.query[:ensure]).to eq(:purged) end it "should consider the package absent if it is marked 'config-files'" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("installed", "config-files") - provider.query[:ensure].should == :absent + expect(provider.query[:ensure]).to eq(:absent) end it "should consider the package absent if it is marked 'half-installed'" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("installed", "half-installed") - provider.query[:ensure].should == :absent + expect(provider.query[:ensure]).to eq(:absent) end it "should consider the package absent if it is marked 'unpacked'" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("installed", "unpacked") - provider.query[:ensure].should == :absent + expect(provider.query[:ensure]).to eq(:absent) end it "should consider the package absent if it is marked 'half-configured'" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("installed", "half-configured") - provider.query[:ensure].should == :absent + expect(provider.query[:ensure]).to eq(:absent) end it "should consider the package held if its state is 'hold'" do Puppet::Util::Execution.expects(:execpipe).with(query_args).yields replace_in_bash_output("install", "hold") - provider.query[:ensure].should == :held + expect(provider.query[:ensure]).to eq(:held) end end @@ -233,12 +233,12 @@ desired ok status next-pkg ensure :DESC: next summary Puppet.expects(:warning).times(4) pipe = StringIO.new(broken_description) - provider_class.parse_multi_line(pipe).should == package_hash + expect(provider_class.parse_multi_line(pipe)).to eq(package_hash) next_package = package_hash.merge(:name => 'next-pkg', :description => 'next summary') hash = provider_class.parse_multi_line(pipe) until hash # warn about bad lines - hash.should == next_package + expect(hash).to eq(next_package) end def parser_test(dpkg_output_string, gold_hash) @@ -246,7 +246,7 @@ desired ok status next-pkg ensure :DESC: next summary Puppet::Util::Execution.expects(:execpipe).with(query_args).yields pipe Puppet.expects(:warning).never - provider.query.should == gold_hash + expect(provider.query).to eq(gold_hash) end it "should parse properly even if delimiter is in version" do @@ -289,10 +289,30 @@ desired ok status name ensure :DESC: summary text no_description = "desired ok status name ensure :DESC: \n:DESC:" parser_test(no_description, package_hash.merge(:description => '')) end + + context "dpkg-query versions < 1.16" do + it "parses dpkg-query 1.15 reporting that package does not exist without warning about a failed match (#22529)" do + Puppet.expects(:warning).never + pipe = StringIO.new("No packages found matching non-existent-package") + Puppet::Util::Execution.expects(:execpipe).with(query_args).yields(pipe).raises(Puppet::ExecutionFailure.new('no package found')) + + expect(provider.query).to eq({:ensure=>:purged, :status=>"missing", :name=>"name", :error=>"ok"}) + end + end + + context "dpkg-query versions >= 1.16" do + it "parses dpkg-query 1.16 reporting that package does not exist without warning about a failed match (#22529)" do + Puppet.expects(:warning).never + pipe = StringIO.new("dpkg-query: no packages found matching non-existent-package") + Puppet::Util::Execution.expects(:execpipe).with(query_args).yields(pipe).raises(Puppet::ExecutionFailure.new('no package found')) + + expect(provider.query).to eq({:ensure=>:purged, :status=>"missing", :name=>"name", :error=>"ok"}) + end + end end it "should be able to install" do - provider.should respond_to(:install) + expect(provider).to respond_to(:install) end describe "when installing" do @@ -303,7 +323,7 @@ desired ok status name ensure :DESC: summary text it "should fail to install if no source is specified in the resource" do resource.expects(:[]).with(:source).returns nil - lambda { provider.install }.should raise_error(ArgumentError) + expect { provider.install }.to raise_error(ArgumentError) end it "should use 'dpkg -i' to install the package" do @@ -373,7 +393,7 @@ desired ok status name ensure :DESC: summary text it "should return the version found by dpkg-deb" do resource.expects(:[]).with(:source).returns "myfile" provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "package\t1.0" - provider.latest.should == "1.0" + expect(provider.latest).to eq("1.0") end it "should warn if the package file contains a different package" do @@ -386,7 +406,7 @@ desired ok status name ensure :DESC: summary text resource = stub 'resource', :[] => "package++" provider = provider_class.new(resource) provider.expects(:dpkg_deb).returns "package++\t1.0" - provider.latest.should == "1.0" + expect(provider.latest).to eq("1.0") end end diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb index 8dd23a935..e438b51a5 100755 --- a/spec/unit/provider/package/rpm_spec.rb +++ b/spec/unit/provider/package/rpm_spec.rb @@ -76,7 +76,7 @@ describe provider_class do installed_packages = subject.instances - installed_packages[0].properties.should == + expect(installed_packages[0].properties).to eq( { :provider => :rpm, :name => "cracklib-dicts", @@ -87,7 +87,8 @@ describe provider_class do :ensure => "2.8.9-3.3", :description => "The standard CrackLib dictionaries", } - installed_packages[1].properties.should == + ) + expect(installed_packages[1].properties).to eq( { :provider => :rpm, :name => "basesystem", @@ -98,7 +99,8 @@ describe provider_class do :ensure => "8.0-5.1.1.el5.centos", :description => "The skeleton package which defines a simple Red Hat Enterprise Linux system", } - installed_packages[2].properties.should == + ) + expect(installed_packages[2].properties).to eq( { :provider => :rpm, :name => "chkconfig", @@ -109,7 +111,8 @@ describe provider_class do :ensure => "1.3.30.2-2.el5", :description => "A system tool for maintaining the /etc/rc*.d hierarchy", } - installed_packages.last.properties.should == + ) + expect(installed_packages.last.properties).to eq( { :provider => :rpm, :name => "mysummaryless", @@ -120,6 +123,7 @@ describe provider_class do :ensure => "1.2.3.4-5.el4", :description => "", } + ) end end @@ -157,7 +161,7 @@ describe provider_class do it "should retrieve version string after querying rpm for version from source file" do resource.expects(:[]).with(:source).returns('source-string') Puppet::Util::Execution.expects(:execfail).with(["/bin/rpm", "-q", "--qf", nevra_format, "-p", "source-string"], Puppet::Error).returns("myresource 0 1.2.3.4 5.el4 noarch :DESC:\n") - provider.latest.should == "1.2.3.4-5.el4" + expect(provider.latest).to eq("1.2.3.4-5.el4") end end @@ -201,7 +205,7 @@ describe provider_class do def parser_test(rpm_output_string, gold_hash, number_of_warnings = 0) Puppet.expects(:warning).times(number_of_warnings) Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).returns(rpm_output_string) - provider.query.should == gold_hash + expect(provider.query).to eq(gold_hash) end let(:resource_name) { 'name' } @@ -258,6 +262,10 @@ describe provider_class do it "should warn but not fail if line is unparseable" do parser_test('bad data', {}, 1) end + + it "should not warn and not fail if rpm returns package not found" do + parser_test('package foo is not installed', {}, 0) + end end describe ".nodigest" do @@ -271,7 +279,7 @@ describe provider_class do describe "when current version is #{version}" do it "should return #{expected.inspect}" do subject.stubs(:current_version).returns(version) - subject.nodigest.should == expected + expect(subject.nodigest).to eq(expected) end end end @@ -287,7 +295,7 @@ describe provider_class do describe "when current version is #{version}" do it "should return #{expected.inspect}" do subject.stubs(:current_version).returns(version) - subject.nosignature.should == expected + expect(subject.nosignature).to eq(expected) end end end diff --git a/spec/unit/provider/parsedfile_spec.rb b/spec/unit/provider/parsedfile_spec.rb index 1ac0c198b..f8a1773de 100755 --- a/spec/unit/provider/parsedfile_spec.rb +++ b/spec/unit/provider/parsedfile_spec.rb @@ -2,8 +2,14 @@ require 'spec_helper' require 'puppet_spec/files' +require 'puppet' require 'puppet/provider/parsedfile' +Puppet::Type.newtype(:parsedfile_type) do + newparam(:name) + newproperty(:target) +end + # Most of the tests for this are still in test/ral/provider/parsedfile.rb. describe Puppet::Provider::ParsedFile do # The ParsedFile provider class is meant to be used as an abstract base class @@ -11,64 +17,68 @@ describe Puppet::Provider::ParsedFile do # sharing data between classes we construct an anonymous class that inherits # the ParsedFile provider instead of directly working with the ParsedFile # provider itself. - subject { Puppet::Type.newtype(:parsedfile_type).provide(:parsedfile_provider, :parent => described_class) } + let(:parsed_type) do + Puppet::Type.type(:parsedfile_type) + end + + let!(:provider) { parsed_type.provide(:parsedfile_provider, :parent => described_class) } describe "when looking up records loaded from disk" do it "should return nil if no records have been loaded" do - subject.record?("foo").should be_nil + provider.record?("foo").should be_nil end end describe "when generating a list of instances" do it "should return an instance for each record parsed from all of the registered targets" do - subject.expects(:targets).returns %w{/one /two} - subject.stubs(:skip_record?).returns false + provider.expects(:targets).returns %w{/one /two} + provider.stubs(:skip_record?).returns false one = [:uno1, :uno2] two = [:dos1, :dos2] - subject.expects(:prefetch_target).with("/one").returns one - subject.expects(:prefetch_target).with("/two").returns two + provider.expects(:prefetch_target).with("/one").returns one + provider.expects(:prefetch_target).with("/two").returns two results = [] (one + two).each do |inst| results << inst.to_s + "_instance" - subject.expects(:new).with(inst).returns(results[-1]) + provider.expects(:new).with(inst).returns(results[-1]) end - subject.instances.should == results + provider.instances.should == results end it "should ignore target when retrieve fails" do - subject.expects(:targets).returns %w{/one /two /three} - subject.stubs(:skip_record?).returns false - subject.expects(:retrieve).with("/one").returns [ + provider.expects(:targets).returns %w{/one /two /three} + provider.stubs(:skip_record?).returns false + provider.expects(:retrieve).with("/one").returns [ {:name => 'target1_record1'}, {:name => 'target1_record2'} ] - subject.expects(:retrieve).with("/two").raises Puppet::Util::FileType::FileReadError, "some error" - subject.expects(:retrieve).with("/three").returns [ + provider.expects(:retrieve).with("/two").raises Puppet::Util::FileType::FileReadError, "some error" + provider.expects(:retrieve).with("/three").returns [ {:name => 'target3_record1'}, {:name => 'target3_record2'} ] Puppet.expects(:err).with('Could not prefetch parsedfile_type provider \'parsedfile_provider\' target \'/two\': some error. Treating as empty') - subject.expects(:new).with(:name => 'target1_record1', :on_disk => true, :target => '/one', :ensure => :present).returns 'r1' - subject.expects(:new).with(:name => 'target1_record2', :on_disk => true, :target => '/one', :ensure => :present).returns 'r2' - subject.expects(:new).with(:name => 'target3_record1', :on_disk => true, :target => '/three', :ensure => :present).returns 'r3' - subject.expects(:new).with(:name => 'target3_record2', :on_disk => true, :target => '/three', :ensure => :present).returns 'r4' + provider.expects(:new).with(:name => 'target1_record1', :on_disk => true, :target => '/one', :ensure => :present).returns 'r1' + provider.expects(:new).with(:name => 'target1_record2', :on_disk => true, :target => '/one', :ensure => :present).returns 'r2' + provider.expects(:new).with(:name => 'target3_record1', :on_disk => true, :target => '/three', :ensure => :present).returns 'r3' + provider.expects(:new).with(:name => 'target3_record2', :on_disk => true, :target => '/three', :ensure => :present).returns 'r4' - subject.instances.should == %w{r1 r2 r3 r4} + provider.instances.should == %w{r1 r2 r3 r4} end it "should skip specified records" do - subject.expects(:targets).returns %w{/one} - subject.expects(:skip_record?).with(:uno).returns false - subject.expects(:skip_record?).with(:dos).returns true + provider.expects(:targets).returns %w{/one} + provider.expects(:skip_record?).with(:uno).returns false + provider.expects(:skip_record?).with(:dos).returns true one = [:uno, :dos] - subject.expects(:prefetch_target).returns one + provider.expects(:prefetch_target).returns one - subject.expects(:new).with(:uno).returns "eh" - subject.expects(:new).with(:dos).never + provider.expects(:new).with(:uno).returns "eh" + provider.expects(:new).with(:dos).never - subject.instances + provider.instances end end @@ -80,22 +90,22 @@ describe Puppet::Provider::ParsedFile do it "returns a resource if the record name matches the resource name" do record = {:name => :one} - subject.resource_for_record(record, resources).should be first_resource + provider.resource_for_record(record, resources).should be first_resource end it "doesn't return a resource if the record name doesn't match any resource names" do record = {:name => :three} - subject.resource_for_record(record, resources).should be_nil + provider.resource_for_record(record, resources).should be_nil 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. - subject.stubs(:retrieve).returns [] - subject.default_target = "/foo/bar" - subject.initvars - subject.prefetch + provider.stubs(:retrieve).returns [] + provider.default_target = "/foo/bar" + provider.initvars + provider.prefetch @filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file") Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype @@ -106,7 +116,7 @@ describe Puppet::Provider::ParsedFile do it "should back up the file being written if the filetype can be backed up" do @filetype.expects(:backup) - subject.flush_target("/my/file") + provider.flush_target("/my/file") end it "should not try to back up the file if the filetype cannot be backed up" do @@ -115,22 +125,51 @@ describe Puppet::Provider::ParsedFile do @filetype.stubs(:write) - subject.flush_target("/my/file") + provider.flush_target("/my/file") end it "should not back up the file more than once between calls to 'prefetch'" do @filetype.expects(:backup).once - subject.flush_target("/my/file") - subject.flush_target("/my/file") + provider.flush_target("/my/file") + provider.flush_target("/my/file") end it "should back the file up again once the file has been reread" do @filetype.expects(:backup).times(2) - subject.flush_target("/my/file") - subject.prefetch - subject.flush_target("/my/file") + provider.flush_target("/my/file") + provider.prefetch + provider.flush_target("/my/file") + end + end + + describe "when flushing multiple files" do + describe "and an error is encountered" do + it "the other file does not fail" do + provider.stubs(:backup_target) + + bad_file = 'broken' + good_file = 'writable' + + bad_writer = mock 'bad' + bad_writer.expects(:write).raises(Exception, "Failed to write to bad file") + + good_writer = mock 'good' + good_writer.expects(:write).returns(nil) + + provider.stubs(:target_object).with(bad_file).returns(bad_writer) + provider.stubs(:target_object).with(good_file).returns(good_writer) + + bad_resource = parsed_type.new(:name => 'one', :target => bad_file) + good_resource = parsed_type.new(:name => 'two', :target => good_file) + + expect { + bad_resource.flush + }.to raise_error(Exception, "Failed to write to bad file") + + good_resource.flush + end end end end @@ -141,7 +180,7 @@ describe "A very basic provider based on ParsedFile" do let(:input_text) { File.read(my_fixture('simple.txt')) } let(:target) { tmpfile('parsedfile_spec') } - subject do + let(:provider) do example_provider_class = Class.new(Puppet::Provider::ParsedFile) example_provider_class.default_target = target # Setup some record rules @@ -158,31 +197,31 @@ describe "A very basic provider based on ParsedFile" do context "writing file contents back to disk" do it "should not change anything except from adding a header" do - input_records = subject.parse(input_text) - subject.to_file(input_records). - should match subject.header + input_text + input_records = provider.parse(input_text) + provider.to_file(input_records). + should match provider.header + input_text end end context "rewriting a file containing a native header" do let(:regex) { %r/^# HEADER.*third party\.\n/ } - let(:input_records) { subject.parse(input_text) } + let(:input_records) { provider.parse(input_text) } before :each do - subject.stubs(:native_header_regex).returns(regex) + provider.stubs(:native_header_regex).returns(regex) end it "should move the native header to the top" do - subject.to_file(input_records).should_not match /\A#{subject.header}/ + provider.to_file(input_records).should_not match /\A#{provider.header}/ end context "and dropping native headers found in input" do before :each do - subject.stubs(:drop_native_header).returns(true) + provider.stubs(:drop_native_header).returns(true) end it "should not include the native header in the output" do - subject.to_file(input_records).should_not match regex + provider.to_file(input_records).should_not match regex end end end diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb index 9782b247d..a6e3f3f12 100755 --- a/spec/unit/settings/file_setting_spec.rb +++ b/spec/unit/settings/file_setting_spec.rb @@ -170,9 +170,7 @@ describe Puppet::Settings::FileSetting do it "should fully qualified returned files if necessary (#795)" do @settings.stubs(:value).with(:myfile).returns "myfile" - path = File.join(Dir.getwd, "myfile") - # Dir.getwd can return windows paths with backslashes, so we normalize them using expand_path - path = File.expand_path(path) if Puppet.features.microsoft_windows? + path = File.expand_path('myfile') @file.to_resource.title.should == path end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 59278d393..7195b0623 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -376,7 +376,11 @@ describe Puppet::Transaction::Report do end it "defaults to serializing to pson" do - expect(Puppet::Transaction::Report.supported_formats).to eq([:pson]) + expect(Puppet::Transaction::Report.default_format).to eq(:pson) + end + + it "supports both yaml and pson" do + expect(Puppet::Transaction::Report.supported_formats).to eq([:pson, :yaml]) end it "can make a round trip through pson" do diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index 812b76438..0c250ae92 100755 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -68,7 +68,7 @@ describe Puppet::Transaction::ResourceHarness do events.map do |event| hash = {} event.instance_variables.each do |varname| - hash[varname] = event.instance_variable_get(varname) + hash[varname.to_sym] = event.instance_variable_get(varname) end hash end diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb index a722d8bff..cc487020c 100755 --- a/spec/unit/util/monkey_patches_spec.rb +++ b/spec/unit/util/monkey_patches_spec.rb @@ -260,19 +260,6 @@ describe Range do end end - -describe Object, "#instance_variables" do - it "should work with no instance variables" do - Object.new.instance_variables.should == [] - end - - it "should return symbols, not strings" do - o = Object.new - ["@foo", "@bar", "@baz"].map {|x| o.instance_variable_set(x, x) } - o.instance_variables.should =~ [:@foo, :@bar, :@baz] - end -end - describe OpenSSL::SSL::SSLContext do it 'disables SSLv2 via the SSLContext#options bitmask' do (subject.options & OpenSSL::SSL::OP_NO_SSLv2).should == OpenSSL::SSL::OP_NO_SSLv2 diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb index 02bfe9490..ba1675981 100755 --- a/spec/unit/util/storage_spec.rb +++ b/spec/unit/util/storage_spec.rb @@ -2,22 +2,14 @@ require 'spec_helper' require 'yaml' +require 'fileutils' require 'puppet/util/storage' describe Puppet::Util::Storage do include PuppetSpec::Files - before(:all) do - @basepath = make_absolute("/somepath") - Puppet[:statedir] = tmpdir("statedir") - end - - after(:all) do - Puppet.settings.clear - end - before(:each) do - Puppet::Util::Storage.clear + @basepath = File.expand_path("/somepath") end describe "when caching a symbol" do @@ -40,7 +32,7 @@ describe Puppet::Util::Storage do end describe "when caching a Puppet::Type" do - before(:all) do + before(:each) do @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :audit => %w{checksum type}) @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") end @@ -81,17 +73,15 @@ describe Puppet::Util::Storage do describe "when the state file/directory does not exist" do before(:each) do - transient = Tempfile.new('storage_test') - @path = transient.path() - transient.close!() + @path = tmpfile('storage_test') end - it "should not fail to load()" do + it "should not fail to load" do FileTest.exists?(@path).should be_false Puppet[:statedir] = @path - proc { Puppet::Util::Storage.load }.should_not raise_error + Puppet::Util::Storage.load Puppet[:statefile] = @path - proc { Puppet::Util::Storage.load }.should_not raise_error + Puppet::Util::Storage.load end it "should not lose its internal state when load() is called" do @@ -101,7 +91,7 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet[:statefile] = @path - proc { Puppet::Util::Storage.load }.should_not raise_error + Puppet::Util::Storage.load Puppet::Util::Storage.state.should == {:yayness=>{}} end @@ -109,9 +99,13 @@ describe Puppet::Util::Storage do describe "when the state file/directory exists" do before(:each) do - @state_file = Tempfile.new('storage_test') - @saved_statefile = Puppet[:statefile] - Puppet[:statefile] = @state_file.path + @state_file = tmpfile('storage_test') + FileUtils.touch(@state_file) + Puppet[:statefile] = @state_file + end + + def write_state_file(contents) + File.open(@state_file, 'w') { |f| f.write(contents) } end it "should overwrite its internal state if load() is called" do @@ -119,55 +113,57 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} - proc { Puppet::Util::Storage.load }.should_not raise_error + Puppet::Util::Storage.load + Puppet::Util::Storage.state.should == {} end it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) + write_state_file(test_yaml.to_yaml) + + Puppet::Util::Storage.load - proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end it "should initialize with a clear internal state if the state file does not contain valid YAML" do - @state_file.write(:booness) - @state_file.flush + write_state_file('{ invalid') + + Puppet::Util::Storage.load + + Puppet::Util::Storage.state.should == {} + end + + it "should initialize with a clear internal state if the state file does not contain a hash of data" do + write_state_file("not_a_hash") + + Puppet::Util::Storage.load - proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do - @state_file.write(:booness) - @state_file.flush - YAML.expects(:load).raises(Puppet::Error) + write_state_file('{ invalid') + File.expects(:rename).raises(SystemCallError) - proc { Puppet::Util::Storage.load }.should raise_error + expect { Puppet::Util::Storage.load }.to raise_error(Puppet::Error, /Could not rename/) end it "should attempt to rename the state file if the file is corrupted" do - # We fake corruption by causing YAML.load to raise an exception - YAML.expects(:load).raises(Puppet::Error) + write_state_file('{ invalid') + File.expects(:rename).at_least_once - proc { Puppet::Util::Storage.load }.should_not raise_error + Puppet::Util::Storage.load end it "should fail gracefully on load() if the state file is not a regular file" do - @state_file.close!() - Dir.mkdir(Puppet[:statefile]) - - proc { Puppet::Util::Storage.load }.should_not raise_error - - Dir.rmdir(Puppet[:statefile]) - end + FileUtils.rm_f(@state_file) + Dir.mkdir(@state_file) - after(:each) do - @state_file.close!() - Puppet[:statefile] = @saved_statefile + Puppet::Util::Storage.load end end end @@ -183,7 +179,8 @@ describe Puppet::Util::Storage do FileTest.exists?(Puppet[:statefile]).should be_false Puppet::Util::Storage.cache(:yayness) - proc { Puppet::Util::Storage.store }.should_not raise_error + Puppet::Util::Storage.store + FileTest.exists?(Puppet[:statefile]).should be_true end @@ -191,19 +188,22 @@ describe Puppet::Util::Storage do Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) - proc { Puppet::Util::Storage.store }.should raise_error + expect { Puppet::Util::Storage.store }.to raise_error Dir.rmdir(Puppet[:statefile]) end it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.state.should == {:yayness=>{}} - proc { Puppet::Util::Storage.store }.should_not raise_error + + Puppet::Util::Storage.store Puppet::Util::Storage.clear + Puppet::Util::Storage.state.should == {} - proc { Puppet::Util::Storage.load }.should_not raise_error + + Puppet::Util::Storage.load + Puppet::Util::Storage.state.should == {:yayness=>{}} end end diff --git a/spec/unit/util/yaml_spec.rb b/spec/unit/util/yaml_spec.rb new file mode 100644 index 000000000..960388ade --- /dev/null +++ b/spec/unit/util/yaml_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +require 'puppet/util/yaml' + +describe Puppet::Util::Yaml do + include PuppetSpec::Files + + let(:filename) { tmpfile("yaml") } + + it "reads a YAML file from disk" do + write_file(filename, YAML.dump({ "my" => "data" })) + + expect(Puppet::Util::Yaml.load_file(filename)).to eq({ "my" => "data" }) + end + + it "writes data formatted as YAML to disk" do + Puppet::Util::Yaml.dump({ "my" => "data" }, filename) + + expect(Puppet::Util::Yaml.load_file(filename)).to eq({ "my" => "data" }) + end + + it "raises an error when the file is invalid YAML" do + write_file(filename, "{ invalid") + + expect { Puppet::Util::Yaml.load_file(filename) }.to raise_error(Puppet::Util::Yaml::YamlLoadError) + end + + it "raises an error when the file does not exist" do + expect { Puppet::Util::Yaml.load_file("no") }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /No such file or directory/) + end + + it "raises an error when the filename is illegal" do + expect { Puppet::Util::Yaml.load_file("not\0allowed") }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /null byte/) + end + + def write_file(name, contents) + File.open(name, "w") do |fh| + fh.write(contents) + end + end +end diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb index 22be8fa7a..48b6e6fc5 100755 --- a/spec/unit/util_spec.rb +++ b/spec/unit/util_spec.rb @@ -59,6 +59,7 @@ describe Puppet::Util do it "should remove any new environment variables after the block ends" do @new_env[:FOO] = "bar" + ENV["FOO"] = nil Puppet::Util.withenv @new_env do ENV["FOO"].should == "bar" end |
